一:概述
View虽然不属于四大组件,但是也有着非常重要的作用。所以深入学习View也是提升Android编程能力的一种有效途径。本blog是我在对View事件分发的实时调试下,对比源码进行分析,所以也希望大家对于源码分析,有自己的思路方法,而不是不注重流程,只是根据其他资料,然后找到相应的源码类阅读即满足。当然,在源码分析的过程中,我也有很多薄弱的地方,希望大家多多提出见解。
二:View基础概念
一个View就是占据了屏幕的一个矩形区域,然后用于绘制显示和事件处理的基础控件(绘制显示,事件处理就是其最核心作用)。
View的层级结构
所有在window中的View都为一个single tree,我们可以通过代码动态添加View,也可以通过XML布局添加,然后就是对于View常见的一些操作为设置Properties,设置(FoucusChange等)listener,Visibility等。
以下是ViewTree:
从IDE结构图中,也可以看出其结构层次:
从这两张图,我们就可以看出View的层级关系。所以不做过多介绍。虽然关于事件分发的View层级相关基础就是以上内容了,但是我认为对于View的位置参数还是值得我们记忆清楚的,如下所示:
View的位置参数:
同样,这张图也能清楚的看到View的四个位置参数,top,bottom,left,right。但是注意,此位置参数相对于View的父容器而言,是一种相对位置参数,而非绝对位置参数。
所以对于View的宽高:
- width = right - left
- height = bottom - top
三:View事件分发
有了基础的概念之后,就思考一个问题,从我们手指触摸屏幕View的那一刻起到View做出相应的的回应(这里进行点击事件的分析)(eg:setOnClickListener,setOnLongClickListener等等的响应),这段时间里发生哪些事情?‘
不过在此之前,我们先来了解MotionEvent这个类,事件分发离不开它,而且还占据很重要的一部分作用(附上官网文档https://developer.android.google.cn/reference/android/view/MotionEvent.html):简单地说这就是一个用action code和axis values来描述movements,以下是常见的action类型(更多完整内容,请大家自行查阅):
基础知识和分发密切相关的MotionEvent都有了了解,接下来就可以来分析View事件的分发机制了。
下面是我在真机调试中,点击Button时检测到的Touch Event并且它的一些时间如图所示:
下面是相关的一些方法调用截图:
从这张图中,我们可以清楚的看到当我手指触摸屏幕后方法所属对象的触发流程为WindowCallvackWrapper ---> Activity ---> PhoneWindow ---> DecorView ---> ViewGroup ---> ..(VG).. ---> View ---> TextView ---> View.onTouchEvent...... ---> AppcompatButton。
总结来说,就是Activity ---> Window ---> View。(关于window,phonewindow,decorview不了解的同志,可以先学习以下,https://blog.csdn.net/keen_zuxwang/article/details/76270432这是我找的一篇博客,仅供参考)
现在我们知道了Touch Event的整体分发流程,会发现里面都是在dispatchTouchEvent,从此方法的名字来看,我们也可以知晓其意。所以接下来我问就需要进入不同阶段相关对象的源码来查看dispatchTouchEvent的源码(关于具体的解释,我都在源代码注释中体现出来,希望大家认真阅读比对):
首先,我们分析Activity的dispatchTouchEvent:
/**
* 当一个点击操作发生时,事件最先传递到的就是Activity,我们从刚才上述的流程图也可以看出来
* 调用以处理触摸屏事件,在将所有触摸屏事件发送到窗口之前,我们可以重写此方法来拦截它们,但是一定要确保实现此事件的正常处理。
*
* @param ev 此触摸屏幕事件
*
* @return boolean 如果这个事件被消耗,则返回true,否则为false
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//可以看出,Activity内部的事件分发机制是交由window处理的
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//当所有View的onTouchEvent都返回false时,Activity的onTouchEvent方法会被调用
return onTouchEvent(ev);
}
然后,我们就可以去看window的dispatchTouchEvent实现了:
/* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
...
public abstract boolean superDispatchTouchEvent(MotionEvent event);
...
}
嗯?是一个抽象类,难搞哦,那还怎么分析?聪明的你或许已经想到了PhoneWindow,经过验证,想法没错,所以我们直接转到PhoneWindow的dispatchTouchEvent源码去分析:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
我们又会发现,其实它也没有处理这次事件分发,直接传递给了DecorView(我们知道其继承自FrameLayout,所以我们知道,事件分发已经进入了View的阶段了,也就是View大阶段的第一个阶段ViewGroup),所以我们只能跟着它的步伐,去看DeocrView的dispatchTouchEvent源码:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
继续向下分析,我们就会看到View的第一阶段ViewGroup的真正dispatchTouchEvent()的实现(其实我觉得自己