前面几节做了这么多铺垫,终于要掀开自定义view的庐山真面目了。
View绘制过程
在android学习5#–自定义View之坐标系统中我就提到过View的显示必须经历Measure(测量)、Layout(布局)和Draw(绘制)过程。具体可以参考官方:How Android Draws Views
- Measure
绘制前通过measure()方法先做一次测量,算出自己view的width和high,官网是这么描述measure()的:The actual measurement work of a view is performed in onMeasure(int, int), called by this method. Therefore, only onMeasure(int, int) can and must be overridden by subclasses. 意思是measure()实际上是通过调用onMeasure()方法来测量。而且子类如果想自己实现测量,只能通过重写onMeasure()方法来实现自定义view的测量。 - Layout
经过测量后,确定再有layout()方法确定在屏幕的位置。
官网如此说:This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent calls layout on all of its children to position them. This is typically done using the child measurements that were stored in the measure pass().
Derived classes should not override this method. Derived classes with children should override onLayout. In that method, they should call layout on each of their children.
主要意思是layout()方法是view绘制的第二步(第一步是测量),同时也提供了onLayout()方法给子类重写。 - Draw
必须经过前面的测量、布局,才能进入绘制。官网如是说:
Manually render this view (and all of its children) to the given Canvas. The view must have already done a full layout before this function is called. When implementing a view, implement onDraw(android.graphics.Canvas) instead of overriding this method. If you do need to override this method, call the superclass version.
意思是说draw()方法会在布局完成后调用,另外需要注意的是自定义view时,可以通过重写onDraw()方法来实现绘制,但是重写onDraw()方法时记得调用父类方法。
View类学习
了解了view的绘制过程后,我们再回到起点,既然我们自定义的view类必须继承view,有必要了解view类的构造函数,掌握了它才知道如何设计自己的view类,
- view():查看源码如此注释说:Non-public constructor for use in testing。用于测试中的非公共构造函数。
- view(Context context):源码如此注释说:Simple constructor to use when creating a view from code.意思是从code上创建时会调用此函数。
- view(Context context, AttributeSet attrs):源码如此注释说:Constructor that is called when inflating a view from XML。意思是view组件是从xml上加载布局绘制时,调用此回调。
- View(Context context, AttributeSet attrs, int defStyleAttr):跟第3种一样,只是增加了style属性设置。通过查看源码可以看出第3中构造函数最后调用的是此构造函数。因此这个接口不会在创建view时自动创建,而是认为手动加载新的style时才会调用。
- View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) :与第4种一样,查看源码发现第3种构造函数实际调用的是此函数。一时不知道如何讲这个函数,后续碰到有用到此构造函数再说吧。
自定义view创建
到此,相信应该或多或少有了一定的概念了,先来看看本例的源码:
public class CustomText extends View {
private Paint mPaint;
private Rect mBound;
private int mTextColor;
private float mTextSize;
private String mTextTitle;
private Bitmap mBg;
private static final String TAG = CustomText.class.getSimpleName();
public CustomText(Context context) {
super(context);
}
public CustomText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomText(Context context, AttributeSet attrs, int defSytleAttr) {
super(context, attrs, defSytleAttr);
TypedArray typeArry = context.obtainStyledAttributes(attrs, R.styleable.CustomText);
Log.d(TAG, "CustomText_texttitle: " + R.styleable.CustomText_texttitle);
Log.d(TAG, "CustomText_textcolor: " + R.styleable.CustomText_textcolor);
Log.d(TAG, "CustomText_textsize: " + R.styleable.CustomText_textsize);
mTextTitle = typeArry.getString(R.styleable.CustomText_texttitle);
mTextColor = typeArry.getColor(R.styleable.CustomText_textcolor, 0xff00ff00);
mTextSize = typeArry.getDimension(R.styleable.CustomText_textsize, 36);
int resourceId = typeArry.getResourceId(R.styleable.CustomText_textbackground, 0);
mBg = BitmapFactory.decodeResource(getResources(), resourceId);
typeArry.recycle();
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
mBound = new Rect();
mPaint.getTextBounds(mTextTitle, 0, mTextTitle.length(), mBound);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
// mBg = Bitmap.createScaledBitmap(mBg, getMeasuredWidth(), getMeasuredHeight(), false);
// canvas.drawBitmap(mBg,0, 0, null);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(110,150,60,mPaint);
mPaint.setColor(Color.YELLOW);
canvas.drawCircle((float)175.5, 210, 60, mPaint);
mPaint.setColor(Color.BLACK);
canvas.drawCircle(245, 150, 60, mPaint);
mPaint.setColor(Color.GREEN);
canvas.drawCircle(311, 210, 60, mPaint);
mPaint.setColor(Color.RED);
canvas.drawCircle(380, 150, 60, mPaint);
mPaint.setColor(mTextColor);
canvas.drawText(mTextTitle, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
}
上面的三个构造函数相信不用再讲解了吧。本节就到这里吧,下节再讲讲如何真正绘制view。
参考:
http://www.jianshu.com/p/84cee705b0d3#
http://blog.youkuaiyun.com/u011733020/article/details/50849475
http://blog.chinaunix.net/uid-26885609-id-3479671.html
http://www.cnblogs.com/angeldevil/p/3479431.html#two
http://blog.youkuaiyun.com/yuzhouxiang/article/details/6958017