Android Activity 触摸屏事件派发机制和源码分析
来源:互联网 发布:黑洞特效源码 编辑:程序博客网 时间:2024/05/17 04:39
Android Activity 触摸屏事件派发机制和源码分析
之前查看View 和ViewGroup的源码的时候就得出结论,ViewGroup的触摸事件是从dispatchTouchEvent()开始,经过一序列判断和处理传递到View的dispatchTouchEvent().当时我们一般看到的页面都是Activity,Activity的触摸事件是怎么出来的呢?
以下分析基于Android L.
1.例子验证
1.1相关代码
自定义的Button和LinearLayout
public class DoovButton extends Button {
private static final String TAG = "DoovButton";
public DoovButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG,"dispatchTouchEvent()" + " action=" + event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG,"onTouchEvent()"+ " action=" + event.getAction());
return super.onTouchEvent(event);
}
}
public class DoovLinearLayout extends LinearLayout {
private static final String TAG = "DoovLinearLayout";
public DoovLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG,"dispatchTouchEvent()"+ " action=" + ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG,"onInterceptTouchEvent()"+ " action=" + ev.getAction());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG,"onTouchEvent()"+ " action=" + event.getAction());
return super.onTouchEvent(event);
}
}
Activity:重写了几个方法和实现了Button和LinearLayout的onClick和onTouch()方法
public class DoovActivity extends Activity implements OnClickListener,OnTouchListener{
private DoovButton mButton;
private DoovLinearLayout mLinearLayout;
private static final String TAG = "DoovActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_doov_view);
mLinearLayout = (DoovLinearLayout)this.findViewById(R.id.doov_ll);
mButton = (DoovButton)this.findViewById(R.id.doov_button);
mLinearLayout.setOnClickListener(this);
mLinearLayout.setOnTouchListener(this);
mButton.setOnClickListener(this);
mButton.setOnTouchListener(this);
Log.d(TAG,"onCreate");
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG,"dispatchTouchEvent" + " action=" + ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG,"onTouchEvent" + " action=" + event.getAction());
return super.onTouchEvent(event);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG,"onTouch" + " action=" + event.getAction() + " v=" + v);
return false;
}
@Override
public void onClick(View v) {
Log.d(TAG,"onClick " +"v=" + v);
}
@Override
public void onUserInteraction() {
Log.d(TAG,"onUserInteraction");
super.onUserInteraction();
}
}
layout布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.example.demo.widget.DoovLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/doov_ll"
android:orientation="vertical" >
<com.example.demo.widget.DoovButton
android:id="@+id/doov_button"
android:text="@string/str_doov_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.example.demo.widget.DoovLinearLayout>
1.2点击button的log
11-21 14:01:24.946: D/DoovActivity(18390): dispatchTouchEvent action=0
11-21 14:01:24.946: D/DoovActivity(18390): onUserInteraction
11-21 14:01:24.946: D/DoovLinearLayout(18390): dispatchTouchEvent() action=0
11-21 14:01:24.946: D/DoovLinearLayout(18390): onInterceptTouchEvent() action=0
11-21 14:01:24.946: D/DoovButton(18390): dispatchTouchEvent() action=0
11-21 14:01:24.946: D/DoovActivity(18390): onTouch action=0 v=com.example.demo.widget.DoovButton{157c21e5 VFED..C. ........ 0,0-264,144 #7f08000d app:id/doov_button}
11-21 14:01:24.946: D/DoovButton(18390): onTouchEvent() action=0
11-21 14:01:25.008: D/DoovActivity(18390): dispatchTouchEvent action=2
11-21 14:01:25.008: D/DoovLinearLayout(18390): dispatchTouchEvent() action=2
11-21 14:01:25.008: D/DoovLinearLayout(18390): onInterceptTouchEvent() action=2
11-21 14:01:25.008: D/DoovButton(18390): dispatchTouchEvent() action=2
11-21 14:01:25.008: D/DoovActivity(18390): onTouch action=2 v=com.example.demo.widget.DoovButton{157c21e5 VFED..C. ...P.... 0,0-264,144 #7f08000d app:id/doov_button}
11-21 14:01:25.008: D/DoovButton(18390): onTouchEvent() action=2
11-21 14:01:25.015: D/DoovActivity(18390): dispatchTouchEvent action=1
11-21 14:01:25.015: D/DoovLinearLayout(18390): dispatchTouchEvent() action=1
11-21 14:01:25.015: D/DoovLinearLayout(18390): onInterceptTouchEvent() action=1
11-21 14:01:25.015: D/DoovButton(18390): dispatchTouchEvent() action=1
11-21 14:01:25.015: D/DoovActivity(18390): onTouch action=1 v=com.example.demo.widget.DoovButton{157c21e5 VFED..C. ...P.... 0,0-264,144 #7f08000d app:id/doov_button}
11-21 14:01:25.015: D/DoovButton(18390): onTouchEvent() action=1
11-21 14:01:25.016: D/DoovActivity(18390): onClick v=com.example.demo.widget.DoovButton{157c21e5 VFED..C. ...P.... 0,0-264,144 #7f08000d app:id/doov_button}
通过以上log我们可以看出,这个和ViewGroup的流程差不多,Down事件经过Activity的
dispatchTouchEvent()之后再由onUserInteraction处理,后面经过DoovLinearLayout的dispatchTouchEvent(), DoovLinearLayout的onInterceptTouchEvent没有拦截就调用了DoovButton的dispatchTouchEvent分发事件,接着调用DoovButton的onTouch方法(在Activity里面实现的),由于DoovButton的onTouch并没有消耗掉这个事件所以继续调用DoovButton的onTouchEvent().- 注意: DoovButton的onTouchEvent()和onClick()消耗掉了时间之后,父控件DoovLinearLayout就不能得到时间了,也就是事件结束了.
1.2点击空白区的log
11-21 14:14:33.724: D/DoovActivity(18390): dispatchTouchEvent action=0
11-21 14:14:33.724: D/DoovActivity(18390): onUserInteraction
11-21 14:14:33.725: D/DoovLinearLayout(18390): dispatchTouchEvent() action=0
11-21 14:14:33.725: D/DoovLinearLayout(18390): onInterceptTouchEvent() action=0
11-21 14:14:33.725: D/DoovActivity(18390): onTouch action=0 v=com.example.demo.widget.DoovLinearLayout{e0b3cdc V.E...C. ........ 0,0-1080,1677 #7f08000c app:id/doov_ll}
11-21 14:14:33.725: D/DoovLinearLayout(18390): onTouchEvent() action=0
11-21 14:14:33.751: D/DoovActivity(18390): dispatchTouchEvent action=2
11-21 14:14:33.751: D/DoovLinearLayout(18390): dispatchTouchEvent() action=2
11-21 14:14:33.751: D/DoovActivity(18390): onTouch action=2 v=com.example.demo.widget.DoovLinearLayout{e0b3cdc V.E...C. ...P.... 0,0-1080,1677 #7f08000c app:id/doov_ll}
11-21 14:14:33.751: D/DoovLinearLayout(18390): onTouchEvent() action=2
11-21 14:14:33.766: D/DoovActivity(18390): dispatchTouchEvent action=2
11-21 14:14:33.766: D/DoovLinearLayout(18390): dispatchTouchEvent() action=2
11-21 14:14:33.766: D/DoovActivity(18390): onTouch action=2 v=com.example.demo.widget.DoovLinearLayout{e0b3cdc V.E...C. ...P.... 0,0-1080,1677 #7f08000c app:id/doov_ll}
11-21 14:14:33.766: D/DoovLinearLayout(18390): onTouchEvent() action=2
11-21 14:14:33.768: D/DoovActivity(18390): dispatchTouchEvent action=1
11-21 14:14:33.769: D/DoovLinearLayout(18390): dispatchTouchEvent() action=1
11-21 14:14:33.770: D/DoovActivity(18390): onTouch action=1 v=com.example.demo.widget.DoovLinearLayout{e0b3cdc V.E...C. ...P.... 0,0-1080,1677 #7f08000c app:id/doov_ll}
11-21 14:14:33.770: D/DoovLinearLayout(18390): onTouchEvent() action=1
11-21 14:14:33.770: D/DoovActivity(18390): onClick v=com.example.demo.widget.DoovLinearLayout{e0b3cdc V.E...C. ...P.... 0,0-1080,1677 #7f08000c app:id/doov_ll}
这个上面也差不多,只是我们现在点击不是Button,而且DoovLinearLayout的其他空白区别,因为点击的范围不在Button的坐标访问之内,所以没有响应button的相关的方法.
上面2种情况都显示只在ACTION_DOWN才调用了Activity的onUserInteraction(),至于为什么,后面分析源码.
2.Hierarchy图
为了更清晰的对Activity的布局层次有个了解,这里截下上面的例子的Hierarchy View
3.Activity源码分析:
肯定是从dispatchTouchEvent()开始
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
/// M: BMW @{
MultiWindowProxy mMultiWindowProxy = MultiWindowProxy.getInstance();
if (MultiWindowProxy.isFeatureSupport() && mMultiWindowProxy != null && isResumed()) {
Slog.v(TAG, "[BMW] dispatchTouchEvent test into isResumed true");
mMultiWindowProxy.moveActivityTaskToFront(mToken);
}
/// @}
}
if (getWindow().superDispatchTouchEvent(ev)) {//如果PhoneWindow把事件分发下去,并被消耗了,就直接返回,否则调用自己的onTouchEvent()
return true;
}
return onTouchEvent(ev);
}
代码相比ViewGroup就显得太短了,这个方法主要分2部分,前面11行主要是处理ACTION_DOWN事件的.后面12-15是处理其他事件的,当然也会处理ACTION_Down事件的.
看第3行的代码就知道为什么前面我们打log分析的时候发现只有ACTION_Down才有调用onUserInteraction(),当时这个方法是个空方法.具体注释看后面.
其中5-10行是mtk 自行添加的(那个M标识的都是mtk自己添加的),暂时也没有搞懂那个MultiWindowProxy的具体作用,暂时不分析.关键是12-15行.
12行 getWindow().superDispatchTouchEvent(ev),这个getWindow()获取的是一个实现了Window接口的对象,其实就是PhoneWindow.这个我是直接搜那个类继承了Window发现就只有一个PhoneWindow,事实也是它.
那么接着看PhoneWindow是怎么实现的superDispatchTouchEvent().
PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
boolean handled = mDecor.superDispatchTouchEvent(event);
if (DBG_MOTION) {
Log.d(TAG, "superDispatchTouchEvent = " + event + ", handled = " + handled);
}
return handled;
}
看起开很短,这个方法的重点就在mDecor.superDispatchTouchEvent(event),而且它决定了superDispatchTouchEvent()的返回值.那mDecor是什么?mDecor就是DecorView,其实mDecor调用自己的
mDecor. superDispatchTouchEvent()也就是下面的代码,继续跟踪发现这个super.dispatchTouchEvent()其实就是调用ViewGroup的dispatchTouchEvent().
ViewGroup.java
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
再回过来看看DecorView发现是PhoneWindow的一个继承了FrameLayout的内部类.这个时候结合我们前面的那个保存的Hierarchy View截图就会发现Activity 的content 区(也就是titile下面的区域)也是一个FrameLayout.其实这个mDecor 就是在Hierarchy View里面id=content的那个FrameLayout,有疑惑的可以打开Activity的setContentView().
这里先介绍一下Activity 和mDecor怎么联系到一起.
我们在Activity里面调用setContentView,其实最终调用的PhoneWindow的setContentView.接着转调PhoneWindow 的installDecor(),接着转调generateLayout.在这个generateLayout()里面会加载一个screen_simple的layout,然后DecorView调用addView来使其变成child view.总之DecorView就是我们在Hierarchy View里面id为content的FrameLayout.
PhoneWindow :generateLayout部分代码如下:
............
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "DecorView-inflate");
View in = mLayoutInflater.inflate(layoutResource, null);//一般情况下:layoutResource=R.layout.screen_simple
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT=com.android.internal.R.id.content
................
所以PhoneWindow在superDispatchTouchEvent里面调用mDecor. superDispatchTouchEvent()的时候也就是开始调用Id为content的FrameLayout的dispatchTouchEvent().到这里就开始了ViewGroup的流程.
ViewGroup的流程参考其他笔记.
总结:如果View不消耗除ACTION_Down以外的其他事件(up/move),那么这个点击事件就会消失,此时父元素的onTouchEvent不会执行,并且当前view可以继续接受后续事件,最终这些消失的点击事件会传递给activity处理.(和ViewGroup的笔记一起总结的.)
其他:
/**
* Called whenever a key, touch, or trackball event is dispatched to the
* activity. Implement this method if you wish to know that the user has
* interacted with the device in some way while your activity is running.
* This callback and {@link #onUserLeaveHint} are intended to help
* activities manage status bar notifications intelligently; specifically,
* for helping activities determine the proper time to cancel a notfication.
*
* <p>All calls to your activity's {@link #onUserLeaveHint} callback will
* be accompanied by calls to {@link #onUserInteraction}. This
* ensures that your activity will be told of relevant user activity such
* as pulling down the notification pane and touching an item there.
*
* <p>Note that this callback will be invoked for the touch down action
* that begins a touch gesture, but may not be invoked for the touch-moved
* and touch-up actions that follow.
*
* @see #onUserLeaveHint()
*/
public void onUserInteraction() {
}
大致的意思:Activity运行过程中,当按键、触屏或者轨迹球事件初始化时被回调。仅有初始化事件发生时,这一方法才被回调,在落键(key down)、击键(key pressed)和起键(key up)时均不会获得任何调用,而仅仅是在用户操作初始化时才获得回调。
0 0
- Android Activity 触摸屏事件派发机制和源码分析
- Android ViewGroup 触摸屏事件派发机制和源码分析
- Android View 触摸屏事件派发机制和源码分析
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)dispatchtouchevent,ontouch,ontouchevent,onclick
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析三(Activity篇)
- Android触摸屏事件派发机制详解与源码分析
- Android View触摸屏事件派发机制详解与源码分析
- Android触摸屏事件派发机制详解与源码分析
- Android触摸屏ViewGroup事件派发机制详解与源码分析
- Android触摸事件派发机制源码分析之Activity
- Android触摸屏事件派发机制详解与源码分析一(View篇)
- 微信公众帐号开发教程第6篇-文本消息的内容长度限制揭秘
- 图的基本存储的基本方式一 邻接矩阵
- 在腾讯云上部署应用
- 优秀csdn博客
- Swift-杂谈篇(2)
- Android Activity 触摸屏事件派发机制和源码分析
- 微信公众帐号开发教程第7篇-文本消息中换行符的使用
- 《第一行代码》学习之旅--第7章-内容提供者ContentProvider
- android studio 基本信息 目录结构
- linux命令特点
- python自动化之路-前端css
- 微信公众帐号开发教程第8篇-文本消息中使用网页超链接
- IoT技术架构
- 编辑pdf文件及校准测量的方法