写点什么

Android 触摸屏事件派发机制详解与源码分析三 (Activity 篇)

用户头像
Android架构
关注
发布于: 17 小时前


2 实例验证



2-1 代码

如下实例与前面实例相同,一个 Button 在 LinearLayout 里,只不过我们这次重写了 Activity 的一些方法而已。具体如下:


自定义的 Button 与 LinearLayout:


public class TestButton extends Button {


public TestButton(Context context, AttributeSet attrs) {


super(context, attrs);


}


@Override


public boolean dispatchTouchEvent(MotionEvent event) {


Log.i(null, "TestButton--dispatchTouchEvent--action="+event.getAction());


return super.dispatchTouchEvent(event);


}


@Override


public boolean onTouchEvent(MotionEvent event) {


Log.i(null, "TestButton--onTouchEvent--action="+event.getAction());


return super.onTouchEvent(event);


}


}


public class TestLinearLayout extends LinearLayout {


public TestLinearLayout(Context context, AttributeSet attrs) {


super(context, attrs);


}


@Override


public boolean onInterceptTouchEvent(MotionEvent ev) {


Log.i(null, "TestLinearLayout--onInterceptTouchEvent--action="+ev.getAction());


return super.onInterceptTouchEvent(ev);


}


@Override


public boolean dispatchTouchEvent(MotionEvent event) {


Log.i(null, "TestLinearLayout--dispatchTouchEvent--action=" + event.getAction());


return super.dispatchTouchEvent(event);


}


@Override


public boolean onTouchEvent(MotionEvent event) {


Log.i(null, "TestLinearLayout--onTouchEvent--action="+event.getAction());


return super.onTouchEvent(event);


}


}


整个界面的布局文件:


<com.example.yanbo.myapplication.TestLinearLayout


xmlns:android="http://schemas.android.com/apk/res/android"


android:layout_width="match_parent"


android:layout_height="match_parent"


android:id="@+id/layout">


<com.example.yanbo.myapplication.TestButton


android:text="click test"


android:layout_width="match_parent"


android:layout_height="wrap_content"


android:id="@+id/button"/>


</com.example.yanbo.myapplication.TestLinearLayout>


整个界面 Activity,重写了 Activity 的一些关于触摸派发的方法(三个):


public class MainActivity extends Activity implements View.OnClickListener, View.OnTouchListener {


private TestButton mButton;


private TestLinearLayout mLayout;


@Override


protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


setContentView(R.layout.activity_main);


mButton = (TestButton) this.findViewById(R.id.button);


mLayout = (TestLinearLayout) this.findViewById(R.id.layout);


mButton.setOnClickListener(this);


mLayout.setOnClickListener(this);


mButton.setOnTouchListener(this);


mLayout.setOnTouchListener(this);


}


@Override


public void onClick(View v) {


Log.i(null, "onClick----v=" + v);


}


@Override


public boolean onTouch(View v, MotionEvent event) {


Log.i(null, "onTouch--action="+event.getAction()+"--v="+v);


return false;


}


@Override


public boolean dispatchTouchEvent(MotionEvent ev) {


Log.i(null, "MainActivity--dispatchTouchEvent--action=" + ev.getAction());


return super.dispatchTouchEvent(ev);


}


@Override


public void onUserInteraction() {


Log.i(null, "MainActivity--onUserInteraction");


super.onUserInteraction();


}


@Override


public boolean onTouchEvent(MotionEvent event) {


Log.i(null, "MainActivity--onTouchEvent--action="+event.getAction());


return super.onTouchEvent(event);


}


}


如上就是实例测试代码,非常简单,没必要分析,直接看结果吧。

2-2 结果分析

直接点击 Button 按钮打印如下:


MainActivity--dispatchTouchEvent--action=0


MainActivity--onUserInteraction


TestLinearLayout--dispatchTouchEvent--action=0


TestLinearLayout--onInterceptTouchEvent--action=0


TestButton--dispatchTouchEvent--action=0


onTouch--action=0--v=com.example.yanbo.myapplication.TestButton


TestButton--onTouchEvent--action=0


MainActivity--dispatchTouchEvent--action=1


TestLinearLayout--dispatchTouchEvent--action=1


TestLinearLayout--onInterceptTouchEvent--action=1


TestButton--dispatchTouchEvent--action=1


onTouch--action=1--v=com.example.yanbo.myapplication.TestButton


TestButton--onTouchEvent--action=1


onClick----v=com.example.yanbo.myapplication.TestButton


分析可以发现,当点击 Button 时除过派发 Activity 的几个新方法之外其他完全符合前面两篇分析的 View 与 ViewGroup 的触摸事件派发机制。对于 Activity 来说,ACTION_DOWN 事件首先触发 dispatchTouchEvent,然后触发 onUserInteraction,再次 onTouchEvent,接着的 ACTION_UP 事件触发 dispatchTouchEvent 后触发了 onTouchEvent,也就是说 ACTION_UP 事件时不会触发 onUserInteraction(待会可查看源代码分析原因)。


直接点击 Button 以外的其他区域:


MainActivity--dispatchTouchEvent--action=0


MainActivity--onUserInteraction


TestLinearLayout--dispatchTouchEvent--action=0


TestLinearLayout--onInterceptTouchEvent--action=0


onTouch--action=0--v=com.example.yanbo.myapplication.TestLinearLayout


TestLinearLayout--onTouchEvent--action=0


MainActivity--dispatchTouchEvent--action=1


TestLinearLayout--dispatchTouchEvent--action=1


onTouch--action=1--v=com.example.yanbo.myapplication.TestLinearLayout


TestLinearLayout--onTouchEvent--action=1


onClick----v=com.example.yanbo.myapplication.TestLinearLayout


怎么样?完全符合上面点击 Button 结果分析的猜想。


那接下来还是要看看 Activity 里关于这几个方法的源码了。


3 Android 5.1.1(API 22) Activity 触摸屏事件传递源码分析




通过上面例子的打印我们可以确定分析源码的顺序,那就开始分析呗。

3-1 从 Activity 的 dispatchTouchEvent 方法说起

3-1-1 开始分析

先上源码,如下:


/**


  • Called to process touch screen events. You can override this to

  • intercept all touch screen events before they are dispatched to the

  • window. Be sure to call this implementation for touch screen events

  • that should be handled normally.

  • @param ev The touch screen event.

  • @return boolean Return true if this event was consumed.


*/


public boolean dispatchTouchEvent(MotionEvent ev) {


if (ev.getAction() == MotionEvent.ACTION_DOWN) {


onUserInteraction();


}


if (getWindow().superDispatchTouchEvent(ev)) {


return true;


}


return onTouchEvent(ev);


}


哎呦!这次看着代码好少的样子,不过别高兴,浓缩才是精华,这里代码虽少,涉及的问题点还是很多的,那么咱们就来一点一点分析吧。


12 到 14 行看见了吧?上面例子咱们看见只有 ACTION_DOWN 事件派发时调运了 onUserInteraction 方法,当时还在疑惑呢,这下明白了吧,不多解释,咱们直接跳进去可以看见是一个空方法,具体下面会分析。


好了,自己分析 15 到 17 行,看着简单吧,我勒个去,我怎么有点懵,这是哪的方法?咱们分析分析吧。


首先分析 Activity 的 attach 方法可以发现 getWindow()返回的就是 PhoneWindow 对象(PhoneWindow 为抽象 Window 的实现子类),那就简单了,也就相当于 PhoneWindow 类的方法,而 PhoneWindow 类实现于 Window 抽象类,所以先看下 Window 类中抽象方法的定义,如下:


/**


  • Used by custom windows, such as Dialog, to pass the touch screen event

  • further down the view hierarchy. Application developers should

  • not need to implement or call this.


*/


public abstract boolean superDispatchTouchEvent(MotionEvent event);


看见注释没有?用户不需要重写实现的方法,实质也不能,在 Activity 中没有提供重写的机会,因为 Window 是以组合模式与 Activity 建立关系的。好了,看完了抽象的 Window 方法,那就去 PhoneWindow 里看下 Window 抽象方法的实现吧,如下:


@Override


public boolean superDispatchTouchEvent(MotionEvent event) {


return mDecor.superDispatchTouchEvent(event);


}


又是看着好简单的样子哦,实际又是一堆问题,继续分析。你会发现在 PhoneWindow 的 superDispatchTouchEvent 方法里又直接返回了另一个 mDecor 对象的 superDispatchTouchEvent 方法,mDecor 是啥?继续分析吧。


在 PhoneWindow 类里发现,mDecor 是 DecorView 类的实例,同时 DecorView 是 PhoneWindow 的内部类。最惊人的发现是 DecorView extends FrameLayout implements RootViewSurfaceTaker,看见没有?它是一个真正 Activity 的 root view,它继承了 FrameLayout。怎么验证他一定是 root view 呢?很简单,不知道大家是不是熟悉 Android App 开发技巧中关于 UI 布局优化使用的 SDK 工具 Hierarchy Viewer。咱们通过他来看下上面刚刚展示的那个例子的 Hierarchy Viewer 你就明白了,如下我在 Ubuntu 上截图的 Hierarchy Viewer 分析结果:



看见没有,我们上面例子中 Activity 中 setContentView 时放入的 xml layout 是一个 LinearLayout,其中包含一个 Button,上图展示了我们放置的 LinearLayout 被放置在一个 id 为 content 的 FrameLayout 的布局中,这也就是为啥 Activity 的 setContentView 方法叫 set content view 了,就是把我们的 xml 放入了这个 id 为 content 的 FrameLayout 中。


赶快回过头,你是不是发现上面 PhoneWindow 的 superDispatchTouchEvent 直接


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


返回了 DecorView 的 superDispatchTouchEvent,而 DecorView 又是 FrameLayout 的子类,FrameLayout 又是 ViewGroup 的子类。机智的你想到了啥木有?


没想到就继续看下 DecorView 类的 superDispatchTouchEvent 方法吧,如下:


public boolean superDispatchTouchEvent(MotionEvent event) {


return super.dispatchTouchEvent(event);


}


这回你一定恍然大悟了吧,不然就得脑补前面两篇博客的内容了。。。


搞半天 Activity 的 dispatchTouchEvent 方法的 15 行if (getWindow().superDispatchTouchEvent(ev))本质执行的是一个 ViewGroup 的 dispatchTouchEvent 方法(这个 ViewGroup 是 Activity 特有的 root view,也就是 id 为 content 的 FrameLayout 布局),接下来就不用多说了吧,完全是前面两篇分析的执行过程。


接下来依据派发事件返回值决定是否触发 Activity 的 onTouchEvent 方法。

3-1-2 小总结一下

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android触摸屏事件派发机制详解与源码分析三(Activity篇)