1.先盗用一张图说一下android的窗口结构:
Activity:可以看做是人与Android机器交互的窗口(就是我们工程中的那个Activity)。
- PhoneWindow:可以看做是Activity与View之间交互的桥梁,Activity通过PhoneWindow与View进行交互,每一个Activity都有一个对应的PhoneWindow。该类继承自Window类,它内部包含了一个DecorView对象。PhoneWindow将DecorView对象进行一定的包装并作为应用窗口的跟View,并且提供通用的窗口操作接口。
- DecorView:PhoneWindow类的内部类,将要显示的View呈现在PhoneWindow上,它是所有应用窗口的根View。它只有一个子元素即一个垂直的LinearLayout。在LinearLayout内部有两个子元素,一个是TitleView(标题)另一个是ContentView(一个FrameLayout),Activity中的setContentView就是设置这个ContentView,而在设置ContentView之前可以通过设置requestWindowFeature(Window.FEATURE_NO_TITLE)取消掉标题。
- 当Activity创建时会在onCreate()方法中调用setContentView()方法,但是我们需要明确的一点就是此方法只是完成了对ContentView的创建,而并没有去执行对View的绘制。
- 那么是谁去负责执行对View绘制的整个流程呢?答案是ViewRoot,每个DecorView都有一个与之对应的ViewRoot,在Activtiy启动时,ActivityFThread.handleResumeActivity()方法里建立了两者的关联,两者建立关联后ViewRoot类的requestLayout()方法会被执行,用来建立用户界面的初次布局,此方法实际上是ViewRootImpl类中的requestLayout()方法
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread();//是否在主线程 mLayoutRequested = true; scheduleTraversals();//调用performTraversals()方法 } }
接下来就进入到了我们日常自定义View中需要用到的三个阶段了 measure(测量大小)
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
. . .
}
layout(判断位置)
public void layout(int l, int t, int r, int b) {
// l为本View左边缘与父View左边缘的距离
// t为本View上边缘与父View上边缘的距离
// r为本View右边缘与父View左边缘的距离
// b为本View下边缘与父View上边缘的距离
...
}
draw(绘制)
public void draw(Canvas canvas) {
. . .
}
当View.draw(Canvas)方法执行完毕后,整个View的绘制流程也就基本完成了。
2.View的事件分发机制
- 在Android中MotionEvent对象是专门为我们在屏幕上的点击、滑动等一系列动作负责的,不管我们在屏幕上进行什么样的操作,无外乎是:
- 点击(ACTION_DOWN) ----- 抬起(ACTION_UP)
- 或者点击(ACTION_DOWN)---- 滑动(ACTION_MOVE)---- 抬起(ACTION_UP)这两种情况
- 一个MotionEvent产生了之后,系统会通过三个比较重要的方法把这个MotionEvent分发给一个具体的View
- public boolean dispatchTouchEvent(MotionEvent event) 它的返回值是一个Boolean值,返回true表示事件被消费,那么此次事件的传递也就会终止了,返回false表示View以及子View均没有消费事件,将调用父View的onTouchEvent()方法。如果一个事件传递给了View,那么此方法一定会被调用。
- public boolean onInterceptTouchEvent(MotionEvent ev) 此方法是一个事件拦截方法,只有在ViewGroup里面才有,View中是没有此方法的。它的返回值也是个Boolean值,返回true表示拦截了事件,拦截之后会调用onTouchEvent()方法,事件不会再向下分发。返回false表示不拦截,事件会继续向下分发,调用子View的dispatchTouchEvent(MotionEvent event)方法。
- public boolean onTouchEvent(MotionEvent ev) 此方法在dispatchTouchEvent(MotionEvent event) 中调用,返回true表示事件消费,终止分发,返回false表示此次事件不消费,调用父View的onTouchEvent(MotionEvent event) 方法.
来张图给大家说明一下:
简单点表达就是产生点击事件,父ViewGroup通过dispatchTouchEvent进行分发,调用onInterceptTouchEvent是否进行拦截,拦截则调用本身的onTouchEvent方法进行消费,消费事件结束,不消费向上传递,直到调用Activity的onTouchEvent方法结束,不拦截则调用子View的dispatchTouchEvent方法继续分发。
对于事件的分发就简单的描述到这里,也算是自己简单的做个笔记,欢迎大家指正