一、概述
自定义View对很多人来说都比较畏惧,认为它比较难,关键还是缺少实践。当你写的多了,见得多了,也就习以为常了,关键是勇敢去尝试。在开发中,我们刚开始很多时候是,公司设计了一种效果,我们立马,去github或者百度再或者其他网站找相同效果,特别是在github上,找到之后直接添加依赖,看一下怎么使用,就完成效果实现。但当我们工作几年之后,还处于找别人添加依赖,对源码不懂,不会自定义,那么无疑会早晚被淘汰。所以今天,个人回顾一下同时写点笔记,对自己也是加强巩固,同时也希望对别人有所帮助。
二、生命周期
清楚的了解View的生命周期,才能更好的自定义view。生命周期包括:
1.创建对象
有两种方式:一直接new,另外一种通过xml布局文件,同时他们所走的方法也有所不同
/**
* new的方式走该方法
* @param context
*/
public Textview(Context context) {
super(context);
}
/**
* xml文件中走该方法
* @param context
* @param attrs
*/
public Textview(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* xml文件 有style时走该方法
* @param context
* @param attrs
* @param defStyleAttr
*/
public Textview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
onFinishInflate() 方法只有布局创建对象方式才会调用,重写的目的是可以得到子View,利用getChildAt(int index)获得子view对象,index是指按照加载顺序排列。
onAttachedToWindow()方法也是创建对象时可以重写的方法,目的也是可以得到字view
2.测量:onMeasure()方法
测量离不开测量模式的介绍,当然测量模式也与父view有关 ,暂时不做解释,测量模式分别是UNSPECIFIED,EXACTLY,AT_MOST。
源码解释为
/**
* Measure specification mode: The parent has not imposed any constraint
* on the child. It can be whatever size it wants.
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* Measure specification mode: The parent has determined an exact size
* for the child. The child is going to be given those bounds regardless
* of how big it wants to be.
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* Measure specification mode: The child can be as large as it wants up
* to the specified size.
*/
public static final int AT_MOST = 2 << MODE_SHIFT;
UNSPECIFIED :表示任意大小,一般我们不会遇到,如ListView,RecyclerView,ScrollView测量子View的时候给的就是UNSPECIFIED ,一般开发中不需要关注它;
EXACTLY :表示一个确定的值,比如在布局中你是这样写的layout_width="100dp","match_parent","fill_parent";
AT_MOST:包裹内容,比如在布局中你是这样写的layout_width="wrap_content"。
另外当mearure()中计算出的视图的宽高就会调用此方法, 在此方法默认保存的视图测量的宽高
注意:视图测量的宽高不等同于视图的宽高。获取的时机不同,重写的意义:得到当前视图/子视图测量的宽高;保存我们自己指定的宽高。mearure()方法不能够被重写。
3.布局:onLayout()方法
在layout()的过程中, 如果某视图的位置改变或强制重新布局就会调用此方法,重写它的意义可以对子View进行重新布局,view.requestLayout() 是强制重新布局的方法。
4.绘制: onDraw()方法
主要用来绘制效果,里面会有一个参数那就是canvas画布,利用canvas就可以画各式各样的效果,如:canvas.drawCircle()画圆形,canvas.drawBitmap()画bitmap,我们这里肯定是需要画文字,那就是drawText()画文本。
@Override
protected void onDraw(Canvas canvas) {
// 画圆
canvas.drawCircle();
// 画bitmap
canvas.drawBitmap();
// 画文本
canvas.drawText();
// ......
}
强制重绘的两个方法
invalidate() :只能在主线程执行
postInvalidate() :可以在主线程或分线程执行
5.事件处理:onTouch()方法
用来处理触摸事件与用户进行交互,MotionEvent.ACTION_DOWN(手指按下)、MotionEvent.ACTION_MOVE(手指移动)、ACTION_UP(手指抬起)。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"ACTION_UP");
break;
}
return super.onTouchEvent(event);
}
当然还有一个比较难以处理的,就是事件分发问题,要看过源码才行。
Paste_Image.png
6.死亡:onDetachedFromWindow()方法
Activity死亡之前,视图对象被移除。
网友评论