2017已至年尾,工作也告一段落了,感觉好久没有更新博客了。工作中遇到了不少问题,好在都一一解决了。
其实关于自定义View这个问题,我一直都感觉到有点头疼,遇到问题的时候总是去百度,或者自己摸索,很是花费时间,本着学习的态度,在此记录一些关于自定义view的探索过程。废话不多说。首先我们了解一下View的生命周期(还是老习惯,先上代码再解释)。
1、一个简单的自定义View(只列举了部分方法):
public class MyTextView extends View { public MyTextView(Context context) { this(context,null); } public MyTextView(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); } public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Log.i("wls", "TextView: " + "TextView"); } @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); Log.i("wls", "onVisibilityChanged: " + "onVisibilityChanged"); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Log.i("wls", "onSizeChanged: " + "onSizeChanged"); } @Override protected void onFinishInflate() { super.onFinishInflate(); Log.i("wls", "onFinishInflate: " + "onFinishInflate"); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i("wls", "onConfigurationChanged: " + "onConfigurationChanged"); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); Log.i("wls", "onMeasure: " + "onMeasure"); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Log.i("wls", "onLayout: " + "onLayout"); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.i("wls", "onDraw: " + "onDraw"); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { Log.i("wls", "onKeyDown: " + "onKeyDown"); return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { Log.i("wls", "onKeyUp: " + "onKeyUp"); return super.onKeyUp(keyCode, event); } }
2、在布局中引用:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.administrator.myview1.MainActivity"> <com.example.administrator.myview1.MyTextView android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="visible"/> </android.support.constraint.ConstraintLayout>
很是简单,运行了一下,发现打印顺序:
02-09 10:16:41.733 18938-18938/com.example.administrator.myview1 I/wls: TextView: TextView
02-09 10:16:41.733 18938-18938/com.example.administrator.myview1 I/wls: onFinishInflate: onFinishInflate
02-09 10:16:41.773 18938-18938/com.example.administrator.myview1 I/wls: onVisibilityChanged: onVisibilityChanged
02-09 10:16:41.773 18938-18938/com.example.administrator.myview1 I/wls: onMeasure: onMeasure
02-09 10:16:41.773 18938-18938/com.example.administrator.myview1 I/wls: onMeasure: onMeasure
02-09 10:16:41.833 18938-18938/com.example.administrator.myview1 I/wls: onMeasure: onMeasure
02-09 10:16:41.833 18938-18938/com.example.administrator.myview1 I/wls: onMeasure: onMeasure
02-09 10:16:41.833 18938-18938/com.example.administrator.myview1 I/wls: onSizeChanged: onSizeChanged
02-09 10:16:41.833 18938-18938/com.example.administrator.myview1 I/wls: onLayout: onLayout
02-09 10:16:41.853 18938-18938/com.example.administrator.myview1 I/wls: onMeasure: onMeasure
02-09 10:16:41.853 18938-18938/com.example.administrator.myview1 I/wls: onMeasure: onMeasure
02-09 10:16:41.853 18938-18938/com.example.administrator.myview1 I/wls: onLayout: onLayout
02-09 10:16:41.853 18938-18938/com.example.administrator.myview1 I/wls: onDraw: onDraw
这里我们按照log打印顺序进行一一解释:
TextView:MyTextView的构造函数,这个没什么好说的,只要你调用的这个View就会执行这个方法,一般会在里面做一些初 始化的东西(如果有自定义属性,也是这构造方法中进行获取);
onFinishInflate:该方法当View及其子View从XML文件中加载完成后会被调用。这个方法没什么意义,紧跟着View的构造方法 执行;
onVisibilityChanged:这个很好理解,只要View是否显示的状态发生变化,就会调用这个方法;
onMeasure:这个算是比较重要的了。这个方法在计算当前View及其所有子View尺寸大小需求时会被调用,一般情况下,在自定义View的时候,都会重写这个方法,onMeasure中有两个参数:widthMeasureSpec,heightMeasureSpec。
系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。
所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:
重写之前先了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
View默认的onMeasure()方法只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就只能使用EXACTLY模式,且控件只可以响应你指定的具体宽高值或者是match_parent属性。如果要让自定义的View支持wrap_content属性,那么就必须重写onMeasure()方法来指定wrap_content时的大小至于这个方法为什么执行那么多次,我也不明白······
onSizeChanged:这个方法是当View的大小发生变化的时候调用;
onLayout: 这个方法是对View视图进行布局,onMeasure对View进行测量,测量之后即可获得View的大小,通过onLayout确定View的位置。onlayout中接收了5个参数:changed,left,top,right,bottom。changed:这个是判断当前View的大小和位置是否发生了变化。剩下的四个参数。left,top,right,bottom就比较容易理解了,指的是View在其父布局ViewGroup中的位置。(这个方法在自定义布局的时候调用)
onDraw:顾名思义,就是绘制的意思,上面通过onMeasure和onLayout两个方法之后,View的大小和位置都确定了,接着就是对View进行绘制了。这里有一个参数canvas:就是画布的意思,一般我们自定义控件的时候,都要通过调用canvas这个参数的方法来实现。这个接下来会详细讲解。
onKeyDown:这个是当view中有控件被按下的时候调用,相应的onKeyUp是手指抬起的时候调用;
canvas的常用操作:
图片来自:(http://blog.csdn.net/u014005316/article/details/54667576)
关于View的声明周期就讲到这里,接下来几篇会通过实例着重理解他们的用法!
这个