View的学习记录
本篇目的
探讨View的绘制过程以及分析View在绘制过程中我们可以参与改造的地方,为自定义View做基础
View的调用
View视图树
Activity中视图的结构
在Activity中,通过setContentView()来设置一个layout布局,在调用后,布局内容才会显示出来
DecorView作为View的根布局来管理所有的View,将视图呈现在了phoneWindow中,DecorView负责View的所有监听事件,通过WindowManagerService来接收,通过Activity对象来回调onClickListener
通过设置requestWindowFeature(Window.Feature.NO_TITLE)来取消appBar
在代码中,setContentView()之后,ActivityManagerService会回调onResume()方法,此时,会把DecorView添加到PhoneWindow中,完成界面绘制.
View的测量
onMeasure()
所有的View都自带一个onMeasure()函数,参数是MeasureSpec类
在onMeasure()方法中,最后,都是通过setMeasuredDimension()方法来保存测量的结果
下面是ViewGroup的测量源码
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
我们可以通过在onMeasure()方法的最后,手动调用setMeasuredDimension()来保存我们自己测量的结果
MeasurSpec类
MeasurSpec是一个设计短小精悍的类
自身是一个32位int值
高2位位测量模式,可以MeasureSpec.getMode()获取
低30位为测量的大小,在计算中,使用的是位计算,为了提高计算速率,可以通过MeasureSpec.getSize()获取
测量模式分三类
- EXACTLY
精确测量模式,对应我们指定控件大小,例如"100dp"或者match_parent这两种情况 - AT_MOST
最大值模式,对应指定控件大小为wrap_content时,随控件内容的变化而变化
还要不超过父控件允许的最大尺寸即可. - UNSPECIFIED
自定义模式
不会指定大小测量模式,View想多大就多大,通常制作自定义View时使用
View类默认的onMeasure()方法只支持EXACTLY模式,自定义View继承自View时,就需要重写onMeasure()方法,拓展对wrap_content模式的支持(否则,默认match_parent)
重写onMeasure()
int MaxSize=200;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
private int measureHeight(int heightMeasureSpec) {
int result=0;
int specMode=MeasureSpec.getMode(heightMeasureSpec);
int specSize=MeasureSpec.getSize(heightMeasureSpec);
if (specMode==MeasureSpec.EXACTLY){
result=specSize;
}else {
result=MaxSize;
if (specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
private int measureWidth(int widthMeasureSpec) {
int result=0;
int specMode=MeasureSpec.getMode(widthMeasureSpec);
int specSize=MeasureSpec.getSize(widthMeasureSpec);
if (specMode==MeasureSpec.EXACTLY){
result=specSize;
}else {
result=MaxSize;
if (specMode==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
View的绘制
测量好View之后,会调用onDraw()方法来绘制View.我们可以重写onDraw()方法,来实现我们想要额外实现的效果
这里就需要了解下系统2D绘图API
在onDraw()方法中,会提供一个Canvas对象,控件就绘制在Canvas上.
画布的创建是通过Bitmap对象,
Canvas canvas=new Canvas(bitmap);
bitmap用来承载canvas上所有的像素信息.
在onDraw(canvas)中,在canva上绘制背景.然后通过Canvas.drawBitmap()来绘制两张画布,在第二张画布上来绘制我们的View,通过改变bitmap来引起View重绘,从而显示改变后的Vbitmap
canVas相当于画布,绘制就需要画笔,系统提供给我们的画笔是Paint及其子类TextPaint等
ViewGrouop的测量和绘制
测量
ViewGroup会管理其子View
当ViewGroup的模式为wrap_content时,就需要遍历所有子View,调用子View自身的Measure()方法后,获得所有子View的大小,来决定自己的大小,其他模式时,会通过具体的指定值来设置自身大小
放置
测量后,需要把子view放置在合适的位置上,就是View的layout过程,同样是遍历子View,调用子View的layout方法,来指定子View的具体位置
绘制
ViewGroup通常情况下不需要绘制,如果背景指定了颜色或者图层,才会触发ViewGroup自身的onDraw()方法.
一般情况下,ViewGroup会调用dispatchDraw()方法,来绘制子View,同样是遍历所有子View,调用子View的Draw()方法.
资料来自于<Android群英传>