自定义View知识总结

来源:互联网 发布:离线下载软件 知乎 编辑:程序博客网 时间:2024/06/05 05:05

参考鸿洋大神的文章

自定义View要做的几件事:

首先参照官方文档

1.自定义View的属性,在xml布局文件中使用该属性

2.在构造方法里面获得我们自定义的属性(两个参数的)

3.[optional]重写onMeasure方法(原因后面讲)

4.重写onDraw()


一步一步来:

1.自定义属性:

在values文件夹下面创建attrs.xml文件,像这样:

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CustomTitleView">        <attr name="mtitleText" format="string"></attr>        <attr name="mtitleTextColor" format="color"></attr>        <attr name="mtitleTextSize" format="dimension"></attr>    </declare-styleable></resources>
这样就声明了三个自定义属性,可以在xml布局文件里使用了。这里说一下,declared-styleable的name最好设置为自定义View的name,为什么,接下来给这个自定义的View的构造函数里面找TypedArray时候好找啊。

ps:使用自定义属性的时候,注意xmlns后面添加的是包名,当然AS中改为auto了。

现在xml布局文件可以这样写了。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:Custom="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity">    <com.example.harris.basement_1115.MyCustonView        android:layout_width="200dp"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        Custom:mtitleTextSize="40sp"        Custom:mtitleText = "Hello"        Custom:mtitleTextColor="#ff03ff"        /></RelativeLayout>
自定义的属性可以以XXX开头。接下来考虑在View的java代码里读取自定义的属性。

开始第二部,在构造方法里面获得我们自定义的属性;



2.在构造方法里面获得我们自定义的属性

一般先用alt+insert快捷键插入构造方法,有三个要重写:

public class SomeView extends View {    public SomeView(Context context) {        super(context);    }    public SomeView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public SomeView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }}
然后我们重写三个参数的构造方法,第一个第二个分别去调用三个参数的构造方法。

三个参数的构造函数长这样:

 public MyCustonView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context,attrs,defStyleAttr);        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView,0, 0);        try {            mTitleTextSize = a.getDimensionPixelSize(R.styleable.CustomTitleView_mtitleTextSize,                    (int) TypedValue.applyDimension                            (TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));            mTitleText = a.getString(R.styleable.CustomTitleView_mtitleText);            mTitleTextColor = a.getColor(R.styleable.CustomTitleView_mtitleTextColor, Color.GREEN);        }        finally {            a.recycle();        }        mPaint = new Paint();        mPaint.setTextSize(mTitleTextSize);        // mPaint.setColor(mTitleTextColor);        mBound = new Rect();        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);        this.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                mTitleText = randomText();                postInvalidate();            }        });    }

生成一个TypedArray对象,调用该对象的getString,getColor以及getDimensionPixelSize等方法获得我们写在xml文件里的自定义属性,赋值给成员变量。


注意这些getXXX方法大部分需要传两个参数,一个是R.styleable.CustomTextView_XXX(注意这个下划线是android自动生成的,根据鸿洋大神的分析,也就是说R.java里面现在有styleable[]这么一个int类型的数组了,数组里面装着CustomTextView_mTextTitleSize、CustomTextView_mTextTitleColor等,final类型的int常量,styleable[CustomTextView_mTextTitleSize]位置具体装的值就是比如说这种东西0x0101014f了,应该是内存地址吧)

不使用declared-styleable的话,你得自己编写一大堆int常量作为数组下标给getString,getDimensionPixel等方法去使用,有点多。。。

,另一个是默认值,如果找不到就返回默认值。


接下来创建绘制对象,官方文档给出了要在构造函数里这么做的原因:

刚开始就创建对象是一个重要的优化技巧。Views会被频繁的重新绘制,初始化许多绘制对象需要花费昂贵的代价。在onDraw方法里面创建绘制对象会严重影响到性能并使得你的UI显得卡顿。

需要创建的有两类:

绘制什么:由Canvas处理

如何绘制:由Paint处理

Simply put, Canvas defines shapes that you can draw on the screen, while Paint defines the color, style, font, and so forth of each shape you draw.

这里我们创建了一个Paint对象,这是必须的,Rect对象是要在onDraw(Canvas canvas)里面交给canvas去使用的。

最后,构造函数里面还添加了监听器。。。。


3.重写onMeasure方法

昨天折腾了一个下午的自定义ViewGroup,onMeasure就不多啰嗦了,

直接上代码

/**针对xml文件中该View可能的measure模式准备了两手:     * 60dp,match_parent(EXACTLY)的时候就使用MeasureSpec去取size     * wrap_content(AT_MOST)的时候就不用MeasureSpec里面的size,重新算     * 最后调用setMeasureDimension完成measure工作。     * @param widthMeasureSpec     * @param heightMeasureSpec     */    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int width;        int height;        if (widthMode == MeasureSpec.EXACTLY) {            width = widthSize;        } else {            mPaint.setTextSize(mTitleTextSize);            mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mBound);            float textWidth = mBound.width();            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());            width =desired;        }        if (heightMode == MeasureSpec.EXACTLY) {            height = heightSize;        } else {            mPaint.setTextSize(mTitleTextSize);            mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mBound);            float texxHeight = mBound.height();            int desired = (int) (getPaddingLeft() + texxHeight + getPaddingRight());            height =desired;        }        setMeasuredDimension(width,height);    }
重写就是为了针对不同的xml里的不同情况多做几手准备。


4.重写onDraw(Canvas canvas)方法
鉴于本人目前对于Canvas、BitMap、Rect一窍不通,直接借用别人的代码

 protected void onDraw(Canvas canvas)    {        mPaint.setColor(Color.GRAY);        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);        mPaint.setColor(mTitleTextColor);        canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);    }
总之就是画了个图。。。。。

引用官方的说法:

What to draw, handled by CanvasHow to draw, handled by Paint.

先这样。






0 0
原创粉丝点击