Android中的控件架构 :
概述:
Android中所有的控件都继承自View类,View为最顶层的类,然后在其之下又有ViewGroup和View两个大的分类,ViewGroup作为父控件可以包含多个View控件,并管理其所包含的View控件,通过ViewGroup,整个界面形成了一个树形结构,也就是常说的控件树。
实例:
那么具体到一个Activity中,这些控件树又是怎么体现的呢?通常情况下,我们在Activity中使用setContentView()来设置自己需要的界面,那他是怎么将我们的布局文件转换为视图的呢?其实对于每一个Activity而言,其总的View层次为下边的这张图:
对于我们而言,当我们调用setContentView()时设置的只是ContentView中的内容。在Activity中都包含一个Window对象,而Window的实现类就是PhoneView,在ContentView中所有的View点击事件都会通过WindowManagerService来进行接受,然后通过Activity对象来回调对应的OnClickListener。在代码中,当Activity中的onCreate()方法被调用之后,ActivityManagerService会调用onResume(),这时候系统才会把整个DecorView添加到PhoneWindow中,并让其显示出来。
View的测量和绘制:
之前说的都是关于整个Android中View的框架概述,接下来就是关于View具体怎么绘制到屏幕上。
View的测量:
View的具体测量是在onMeasureSpec()这个方法中完成的。同时Android中也提供给我们用于测量的辅助类MeasureSpec。MeasureSpec类是一个32位的int值,其中高两位为测量的模式,低30位是测量的大小。
View的测量模式:
EXACTLY:精确值模式,当我们为控件定义具体的宽高属性时,或者是沾满父控件时使用该测量模式。
AT_MOST:最大值模式,当控件的宽高属性为warp_content时,控件大小一般由控件中的具体内容决定,这时只要该控件不超过父控件的大小即可。
UNSPECIFIED:该模式不指定View大小的测量模式,通常使用自定义View时才会使用该模式。
在自定义View时需要重写View的onMeasure()方法,他默认是调用了父类中的onMeasure()方法。
而父类中的onMesure()方法则是通过setMeasureDimension(),方法来设置View的尺寸。所以我们可以通过在onMeasure()重新计算View的长和宽,然后将其通过setMeasureDimension()方法来修改控件的尺寸。
在onMeasure()中,我们需要根据测量模式来进行不同的计算:
当测量模式为EXACTLY时,直接使用给定的尺寸值,
当测量模式为UNSPECIFIED时,需要给View一个默认的大小,
当测量模式为AT_MOST时,需要将默认的大小和测量出来的最小值进行比较,取其中较小值作为结果。
/**
* 重写测量方法
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
/**
* 默认的View大小
*/
public static final int DEFAULT_SIZE = 200;
/**
* 自定义的测量高度的方法
* @param heightMeasureSpec
* @return
*/
private int measureHeight(int heightMeasureSpec) {
return measure(heightMeasureSpec);
}
/**
* 自定义的测量宽度的方法
* @param widthMeasureSpec
* @return
*/
private int measureWidth(int widthMeasureSpec) {
return measure(widthMeasureSpec);
}
/**
* 自定义的测量方法
* @param measureSpec
* @return
*/
private int measure(int measureSpec) {
//获取测量模式和测量值
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
//最后的测量值
int size = DEFAULT_SIZE;
//根据不同的测量模式,有不同的测量结果
if (specMode == MeasureSpec.EXACTLY) {
size = specSize;
}else{
size = DEFAULT_SIZE;
if (specMode == MeasureSpec.AT_MOST) {
size = Math.min(specSize,size);
}
}
return size;
}
View的绘制:
在将View测量完之后就需要根据要求进行View的绘制,达到我们需要的效果。
关于绘制的详细讲解可以看之前的文章:
在View中重写onDraw()方法,并在其中进行图像的绘制。
ViewGroup的测量和绘制:
ViewGroup的测量:
ViewGroup的测量和View的测量原理相似,只是需要对子View需要遍历测量。
当ViewGroup设置为warp_content时,需要遍历所有的子View,从而决定自己的大小,同样需要重写ViewGroup的onMeasure()方法。
当ViewGroup设置为其他具体的值时就根据具体的值来设置自己的大小。
注:在ViewGroup测量自身尺寸时,会依次调用子View的onMeasure方法来获取测量结果
ViewGroup的布局:
在测量完成之后就需要将所有的子View放在合适的位置,这时候就会通过View的onLayout()方法来为每个子控件找到合适的位置。在布局时同样是通过遍历调用子View的onLayout()方法,指定具体的显示的位置,从而决定布局位置。
ViewGroup的绘制:
ViewGroup自己的绘制任务并不是很重,若不是为其指定了背景色,其甚至不会调用自身的onDraw()方法,在该步骤中,ViewGroup最重要的方法是dispatchDraw()方法,即通过这个方法来遍历调用子View的绘制方法,从而完成绘制。
这些就是一些关于自定义View之前需要了解的基础知识。
相关链接: