android中几乎所有控件都直接或者间接是view的子类,包括布局中的线性布局,常用的Button等。
在android中用户消息类型分为:
首先,在ViewRoot中定义了一个InputHandler对象,当底层得到按键消息后,会回调到该InputHandler对象的handleKey()函数,该函数再调用ViewRoot中的dispatchKey(),该函数内部发出一个异步DISPATCH_KEY消息,消息处理函数为deliverEvent(),该函数内部分以下三个步骤执行:
1. 调用mView.dispatchKeyEventPreIme()。对于View系统来讲,如何有输入法窗口存在,会先将按键消息派发到输入法窗口,只有当输入法窗口没有处理该消息时,才会把消息继续派发到真正的视图.
2. 接下来就需要把该消息派发到输入法窗口,当然,此时输入法窗口必须存在,如果不存在则派发给真正的视图。
3. 调用deliverKeyEventToViewHierarchy(),将消息派发给真正的视图。
1、进行物理像素到逻辑像素的转换。比如800*480的像素分辨率的屏幕,操作系统却将其定义为480*320像素,触摸消息本对应物理屏幕,所以要转成系统逻辑坐标。
2、如果是DOWN消息,则调用ensureTouchMode(true)函数进入触摸模式。
3、将屏幕坐标转换成视图坐标(X,Y坐标系)
4、调用mView.dispatchTouchEvent()将消息派发给根视图。
5、如果以上视图和子视图都没有消耗该消息,则处理屏幕边界偏移
1、调用onFilterTouchEventForSecurity()处理窗口处于模糊显示状态下的消息。
2、调用视图监听者的onTouch()函数,如果监听者消耗了该消息,则直接返回。
3、调用onTouchEvent(),应用程序可以重载该函数,但如果没有重载的话,该函数有其内部的执行方式。
4、处理ACTION_CANCEL消息,这里只需要清除PRESSED标识,刷新视图状态,然后再关闭tap监测即可。
遍历View树意味着整个View需要对其包含的子视图分配大小并重绘。在一般情况下,导致重新遍历的原因主要有三个:
1、视图本身内部状态变化引起重绘
2、View内部添加或者删除View
3、View本身大小和可见性发生变化
导致View树重新遍历的总体诱因
1、refreshDrawableList()
2、onFocusedChanged()
3、ensureTouchMode()
4、setVisibility()
5、setEnable()
6、setSelected()
7、invalidate()
8、requestFocus()
9、requestLayout()
measure是测量的意思,那么onMeasure()方法顾名思义就是用于测量视图的大小的。View系统的绘制流程会从ViewRoot的performTraversals()方法中开始,在其内部调用View的measure()方法。measure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的规格和大小。
MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。specMode一共有三种类型,如下所示:
”确定是“:表示父视图希望子视图大小应该是specSize中指定的大小
“最大是”:子视图的大小最大是specSize中指定的大小。
“没有限制”:此时View的设计者可以根据自身的特性设置视图的大小。
这个方法是给视图真正的布局过程。
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
参数指定子视图在父视图中左,上,右,下的位置。
把view用canvas绘制到屏幕上。ViewRoot中的代码会继续执行并创建出一个Canvas对象,然后调用View的draw()方法来执行具体的绘制工作。
public class MyView extends View {
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
System.out.println("MyView(Context context, AttributeSet attrs)");
}
public MyView(Context context) {
super(context);
System.out.println("MyView(Context context)");
}
/** * 一个view 从创建到显示在屏幕上的主要步骤: * * 1、调用构造方法创建对象 * * 2、测量view的大小 * onMeasure(int,int); // 用于测量view的大小 * * 3、确定view的位置 * onLayout(boolean,int,int,int,int); * * 4、绘制view的内容 * onDraw(Cancas); * */
@Override
// 当系统测量view的大小的时候,调用
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 我们必须告诉系统,这个view有多大,通过调用setMeasuredDimension(width,height)
setMeasuredDimension(200,80);
}
@Override
/** * 当系统指定view的位置后,回调 该方法 ,view的位置,由父view决定,子view只有建议权,没有决定权 * @params changed 当前view的尺寸和位置,是否发生变化 * @params left,top,right,bottom 当前view在父view中的位置 * */
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
System.out.println(left+" : "+top+" : "+ right+" : "+bottom);
}
@Override
/** * 绘制view的内容 */
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
}
}