一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP 当屏幕中包含一个ViewGroup(或者Layout,实际上layout也是viewGroup),而这个ViewGroup又包含一个子view(也可能是viewGroup,此处我们仅讨论view),这个时候系统如何处理Touch事件呢?到底是 ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?他们的执行过程是怎么样的呢? 我将以实例进行说明: 这里我自定义一个MyLinearLayout继承至LinearLayout,定义一个MyTextView继承至TextView. MyLinearLayout.java - package com.example.testforevent;
-
- import android.content.Context;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.widget.LinearLayout;
-
- public class MyLinearLayout extends LinearLayout {
-
- private static final String TAG = "MyLinearLayout";
- // //说明:提到的子viewactivity_main.xml中的MyTextView,当前viewGroup是指activity_main.xml的MyLinearLayout(因为linearLayout实际上也是viewGroup,这里是MyLinearLayout)
- public MyLinearLayout(Context context) {
- super(context);
- }
-
- public MyLinearLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- }
- //dispatchTouchEvent,onInterceptTouchEvent,TouchEvent三者的触发顺序是dispatch最先,intercept
- //次之,Touch最后
- //返回值为true时当event传到这里时不再下发:情况一仅点击当前所在viewGroup:事件触发顺序:dispatchTouchEvent .actionDown->
- //onInterceptTouchEvent.actionDown->onTouchEvent.actionDown->dispatchTouchEvent.actionUp->
- //onTouchEvent.actionUp
- //情况二:点击其子view(这里指当中的MyTextView)。当手指按下屏幕是,事件的触发属性是:
- //layout的dispatchTouchEvent.action_down->layout的onInterceptTouchEvent.action_down->子View的dispatchTouchEvent.action_down
- //->子view的onTouchEvent.action_down->layout的onTouchEvent.action_down->layout的dispatchTouchEvent.actionUp->layout的
- //onTouchEvent.actionUp
- //返回值为false时,会依次触发action_down,事件会继续分发下去
- @Override
- public boolean onTouchEvent(MotionEvent event) {
-
- int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "onTouchEvent: action_cancel");
- break;
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "onTouchEvent: action_down");
- break;
- case MotionEvent.ACTION_MASK:
- Log.d(TAG, "onTouchEvent: action_mask");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "onTouchEvent: action_move");
- break;
- case MotionEvent.ACTION_OUTSIDE:
- Log.d(TAG, "onTouchEvent: action_outside");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "onTouchEvent: action_up");
- break;
- default:
- break;
- }
- return super.onTouchEvent(event);
- }
- //触发优先级最高,当返回值为true时,事件不再下发(不再传递给onIntercepteEvent,TouchEvent,子view也不会接收到任何事件),即拦截了event
- //若未拦截,事件继续下发,依次的传递顺序是:dispatch->onIntercepteEvent->子view的dispatchEvent->子view的touchEvent->TouchEvent
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "dispatchTouchEvent: action_cancel");
- break;
- //一个简单的单击屏幕事件,一定会最先触发down,手指一按下就触发
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "dispatchTouchEvent: action_down");
- break;
- case MotionEvent.ACTION_MASK:
- Log.d(TAG, "dispatchTouchEvent: action_mask");
- break;
- //若是拦截事件(返回true),在手指存在移动时,将会触发,手指不动,将不会不触发
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "dispatchTouchEvent: action_move");
- break;
- case MotionEvent.ACTION_OUTSIDE:
- Log.d(TAG, "dispatchTouchEvent: action_outside");
- break;
- //若是拦截事件(返回为true),手指离开屏幕时触发
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "dispatchTouchEventt: action_up");
- break;
- default:
- break;
- }
- // return true;
- return super.dispatchTouchEvent(ev);
- }
- //当接收到从dispatch传递过来的event,将触发action_down事件。返回true时将窃取子view的
- //motionEvent事件,但不会影响当前所在view组的TouchEvent的触发,即子view不会触发子view的
- //dispatchEvent,TouchEvent;返回为false时,事件将继续下发,子view将触发先后触发子view的dispatchEvent
- //和TouchEvent,然后再触发当前所在viewGroup的onTouchEvent
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
-
- int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "onInterceptTouchEvent: action_cancel");
- break;
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "onInterceptTouchEvent: action_down");
- break;
- case MotionEvent.ACTION_MASK:
- Log.d(TAG, "onInterceptTouchEvent: action_mask");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "onInterceptTouchEvent: action_move");
- break;
- case MotionEvent.ACTION_OUTSIDE:
- Log.d(TAG, "onInterceptTouchEvent: action_outside");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "onInterceptTouchEvent: action_up");
- break;
- default:
- break;
-
- }
-
-
- return super.onInterceptTouchEvent(ev);
- }
-
- }
MyTextView.java - package com.example.testforevent;
-
- import android.content.Context;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.widget.TextView;
-
- public class MyTextView extends TextView {
-
- private final static String TAG = "MyTextView";
-
- public MyTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public MyTextView(Context context) {
- super(context);
- }
- //返回值true时,layout的onTouchEvent不会触发,被子view拦截了。此时执行的是Layout的 dispatchTouchEvent: action_down
- //->Layout的 onInterceptTouchEvent: action_down->子view的dispatchTouchEvent: action_down->子View的 onTouchEvent: action_down
- //->Layout的 dispatchTouchEventt: action_up->Layout的onInterceptTouchEvent: action_up->
- //子View的 dispatchTouchEventt: action_up->子View的OnTouchEventt: action_up
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "onTouchEvent: action_cancel");
- break;
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "onTouchEvent: action_down");
- break;
- case MotionEvent.ACTION_MASK:
- Log.d(TAG, "onTouchEvent: action_mask");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "onTouchEvent: action_move");
- break;
- case MotionEvent.ACTION_OUTSIDE:
- Log.d(TAG, "onTouchEvent: action_outside");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "onTouchEvent: action_up");
- break;
- default:
- break;
- }
- return true;
- //return super.onTouchEvent(event);
- }
- //当返回值为true时,子view的TouchEvent和layout的TouchEvent都不会触发。依次触发的过程是:layout的
- //dispatchTouchEvent.actionDown->layout的onInterceptTouchEvent.actionDown->子view的dispatchTouch
- //Event.actionDown->layout的dispatchTouchEvent.actionUp->layout的onInterceptTouchEvent.actionUp->
- //子view的dispatchTouchEvent.actionUp
- //
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
-
- int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- Log.d(TAG, "dispatchTouchEvent: action_cancel");
- break;
- case MotionEvent.ACTION_DOWN:
- Log.d(TAG, "dispatchTouchEvent: action_down");
- break;
- case MotionEvent.ACTION_MASK:
- Log.d(TAG, "dispatchTouchEvent: action_mask");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.d(TAG, "dispatchTouchEvent: action_move");
- break;
- case MotionEvent.ACTION_OUTSIDE:
- Log.d(TAG, "dispatchTouchEvent: action_outside");
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "dispatchTouchEventt: action_up");
- break;
- default:
- break;
- }
-
- return super.dispatchTouchEvent(event);
- }
-
- }
MainActivity.java - package com.example.testforevent;
-
- import android.os.Bundle;
- import android.app.Activity;
- import android.view.Menu;
- import android.view.MenuItem;
- import android.support.v4.app.NavUtils;
-
- public class MainActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.activity_main, menu);
- return true;
- }
- //一个activity中view触发的顺序是外层先获取event事件,然后向内传递给里面包含的子view,若是在外层layout
- //拦截了event事件,那么不会再传递给子view,子view的TouchEvent将不会触发
-
- }
activity_main.xml - <com.example.testforevent.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <com.example.testforevent.MyTextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:padding="@dimen/padding_medium"
- android:text="@string/hello_world"
- tools:context=".MainActivity" />
-
- </com.example.testforevent.MyLinearLayout>
先看看运行的效果截图吧: 1.a.点击layout b.单击textview 2.仅当MyLinearLayout的dispatchTouchEvent返回值为true时: 1>.单击layout(不点击到textview上) 2>.单击textview(textView的所有touch事件都不会触发) 3.仅当MyLinearLayout的dispatchTouchEvent返回值为true,它将偷取子view的touch事件,也就是说子view不会接受到任何Touch事件 1>.单击layout 2.>单击textview(事件不分发到子view),看到的结果与点击layout相同,说明子view没有接收到event 4.仅当MyLinearLayout的onTouchEvent返回值为true时 1>点击layout 2>.点击textview,textview的接收到了事件 5.仅当TextView的DispatchTouchEvent返回值为true时,点击textview,textview的onTouchEvent不会被触发 6.仅当TextView的onTouchEvent返回值为true时,layout的onTouchEvent不会被触发 在ViewGroup(即上面Layout)情况特别分析: 针对由于触摸(Touch)而触发的事件。 Android的Touch的第一个状态肯定是ACTION_DOWN, 表示按下了屏幕。之后,touch将会有后续事件,可能是: ACTION_MOVE //表示为移动手势 ACTION_UP //表示为离开屏幕 ACTION_CANCEL //表示取消手势,不会由用户产生,而是由程序产生的 一个Action_DOWN, n个ACTION_MOVE, 1个ACTION_UP,就构成了Android中众多的事件。 在Android中,有一类控件是中还可以包含其他的子控件,这类控件是继承于ViewGroup类,例如:ListView, Gallery, GridView。 还有一类控件是不能再包含子控件,例如:TextView。 对于ViewGroup类的控件,有一个很重要的方法,就是onInterceptTouchEvent(),用于处理事件并改变事件的传递方向,它的返回值是一个布尔值,决定了Touch事件是否要向它包含的子View继续传递,这个方法是从父View向子View传递。(返回值为true时,不会向子view传递事件) 而方法onTouchEvent(),用于接收事件并处理,它的返回值也是一个布尔值,决定了事件及后续事件是否继续向上传递,这个方法是从子View向父View传递。(为true时不会向上传递) Touch事件在 onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。返回值为true表示事件被正确接收和处理了,返回值为false表示事件没有被处理,将继续传递下去(只是传递方向不一样,onInterceptTouchEvent()向子View传,而onTouchEvent()向父View传)。 具体情况如下: ACTION_DOWN事件会传到某个ViewGroup类的onInterceptTouchEvent,如果返回false,则DOWN事件继续向子ViewGroup类的onInterceptTouchEvent传递,如果子View不是ViewGroup类的控件,则传递给它的onTouchEvent。 如果onInterceptTouchEvent返回了true,则DOWN事件传递给它的onTouchEvent,不再继续传递,并且之后的后续事件也都传递给它的onTouchEvent。 如果某View的onTouchEvent返回了false,则DOWN事件继续向其父ViewGroup类的onTouchEvent传递;如果返回了true,则后续事件会直接传递给其onTouchEvent继续处理。(后续事件只会传递给对于必要事件ACTION_DOWN返回了true的onTouchEvent) onInterceptTouchEvent可以接受到所有的Touch事件,而onTouchEvent则不一定。 |