android里面和touch相关的方法最常见的有四个:onTouch,dispatchTouchEvent,onTouchEvent,如果是一个GroupView的话还有一个onInterceptTouchEvent。
这四个方法有什么关系?很多新手搞不明白,我在网上找了很多的资料,发现自己的研究结果与资料上的有出入。所以将自己的结论写出来,Android的事件传递机制到底是怎么样的,也可以由此一探究竟。
我这例子实在网上的例子改造的,原来文章的链接:http://www.blogjava.net/lzqdiy/archive/2011/05/08/349794.html
先贴出我的代码,在做仔细的分析
<?xml version="1.0" encoding="utf-8"?> <view android:layout_width="fill_parent" android:layout_height="fill_parent" class="com.example.AndroidTouchTest.MyLinearLayout" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/view"> <com.example.AndroidTouchTest.MyTextView android:layout_width="200px" android:layout_height="200px" android:id="@+id/tv" android:text="lzqdiy" android:textSize="40sp" android:textStyle="bold" android:background="#FFFFFF" android:textColor="#0000FF"/> </view>
package com.example.AndroidTouchTest; import android.app.Activity; import android.os.Bundle; public class MyActivity extends Activity { /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
这里的代码编辑器不太好用,想看代码的可以到这里:http://www.cnblogs.com/HighFun/archive/2013/03/26/2979901.html
点击textiew的时候,会看到下面的打印:
03-23 17:22:23.545: DEBUG/MyLinearLayout(2459): dispatchTouchEvent action:ACTION_DOWN
03-23 17:22:23.545: DEBUG/MyLinearLayout(2459): onInterceptTouchEvent action:ACTION_DOWN
03-23 17:22:23.545: DEBUG/MyLinearLayout(2459): onInterceptTouchEvent action: flag false
03-23 17:22:23.545: DEBUG/MyTextView(2459): dispatchTouchEvent action:ACTION_DOWN
03-23 17:22:23.545: DEBUG/MyTextView(2459): onTouch action:ACTION_DOWN
03-23 17:22:23.545: DEBUG/MyTextView(2459): onTouch action: FORCE SET flag false
03-23 17:22:23.545: DEBUG/MyTextView(2459): ---onTouchEvent action:ACTION_DOWN
03-23 17:22:23.545: DEBUG/MyTextView(2459): ---onTouchEvent action: flag false
03-23 17:22:23.545: DEBUG/MyTextView(2459): onTouchEvent action: flag false
03-23 17:22:23.545: DEBUG/MyLinearLayout(2459): onTouch action:ACTION_DOWN
03-23 17:22:23.545: DEBUG/MyLinearLayout(2459): onTouch action: FORCE SET flag false
03-23 17:22:23.545: DEBUG/MyLinearLayout(2459): ---onTouchEvent action:ACTION_DOWN
03-23 17:22:23.554: DEBUG/MyLinearLayout(2459): ---onTouchEvent action: flag false
03-23 17:22:23.554: DEBUG/MyLinearLayout(2459): dispatchTouchEvent action: flag false
调用顺序整理如下:颜色表示控件,缩进表示调用关系。
MyLinearLayout(2459): dispatchTouchEvent
MyLinearLayout(2459): onInterceptTouchEvent
MyTextView(2459): dispatchTouchEvent action
MyTextView(2459): onTouch
MyTextView(2459): ---onTouchEvent
MyLinearLayout(2459): onTouch
MyLinearLayout(2459): ---onTouchEvent
MyLinearLayout(2459): dispatchTouchEven
这个是什么意思呢?
首先我们要分析下这几个函数的作用和返回值的意义
dispatchTouchEvent是用来分发事件的,控件的事件的分发都是通过这个函数来完成的。
onInterceptTouchEvent是判断是否截取事件,为什么要截取事件呢?因为控件重要有可能包含其他的控件,比如说,本例子代码中linearlayout中包含了一个textiew。他的作用就是决定这个事件是否需要传递給子控件。不过注意仅仅是GroupView的控件才有哦,有些控件,像textview就没法办法包含子控件的,所以它就没有这个方法。
ontouch是事件监听器的方法,只要有触摸的操作就是有这个方法。
onTouchEvent是触摸事件发生以后产生的处理。
他们都有一个boolean的返回值,false表示该事件需要继续传播,true表示该事件不用继续传播了。
每点击一次屏幕会有三个事件发生:ACTION_DOWN, ACTION_MOVE, ACTION_UP。 这里可以看到如果dispatchTouchEvent返回值是false的话,则不会再有后续的事件,后续的事件被丢弃掉了。
LinearLayout的调用函数图
LinearLayout的 dispatchTouchEvent代码
Textiew的调用流程
1 public boolean dispatchTouchEvent(MotionEvent event) { 2 if (mInputEventConsistencyVerifier != null) { 3 mInputEventConsistencyVerifier.onTouchEvent(event, 0); 4 } 5 6 if (onFilterTouchEventForSecurity(event)) { 7 //noinspection SimplifiableIfStatement 8 ListenerInfo li = mListenerInfo; 9 if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED 10 && li.mOnTouchListener.onTouch(this, event)) { 11 return true; 12 } 13 14 if (onTouchEvent(event)) { 15 return true; 16 } 17 } 18 19 if (mInputEventConsistencyVerifier != null) { 20 mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); 21 } 22 return false; 23 }
可以看到TextView的dispatchTouchEvent先调用的Ontouch(9,10行)如果调用结果返回true则返回,如果是false则继续调用Ontouchevent。
正式基于这两种传输机制,事件得以在控件中不断传输。
那么事件是怎么通过父控件传入到子控件呢?
1 while (target != null) { 2 final TouchTarget next = target.next; 3 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { 4 handled = true; 5 } else { 6 final boolean cancelChild = resetCancelNextUpFlag(target.child) 7 || intercepted; 8 if (dispatchTransformedTouchEvent(ev, cancelChild, 9 target.child, target.pointerIdBits)) { 10 handled = true;
这是在LinearLayout中的代码段,可以看到调用了dispatchTransformedTouchEvent方法,代码比较长,只给出一部分。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. final int oldAction = event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } …………
可以看到会寻找子控件,并且调用子控件的dispatchTouchEvent,如果没有就调用父类(不是父控件)的dispatchTouchEvent。而ViewGroup的父类就是View。
机制就是这样,不同的返回值会有不同的调用顺序,但是原理都是一样的。可以修改没有方法的返回值来查看代码的调用情况。
源代码地址:https://github.com/Dothegod/AndroidTouchTest/