zoukankan      html  css  js  c++  java
  • android Touch事件传递小结

    这次还是先贴上测试代码吧。。

    主布局文件是个三层结构,最外层和中间层都是LinearLayout的子类,里层是个TextView:

    <com.example.touchevent.OutterLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/outter_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.touchevent.MainActivity"
        tools:ignore="MergeRootFrame" 
        android:orientation="vertical"
        >
        <com.example.touchevent.InnerLayout
            android:id="@+id/inner_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="50dp"
            android:orientation="vertical"
            android:background="#ff0"
            android:layout_gravity="center"
            >
            <com.example.touchevent.MyTextView
                android:id="@+id/text_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="50dp"
                android:background="#0ff"
                android:layout_gravity="center"
                />
        </com.example.touchevent.InnerLayout>
    </com.example.touchevent.OutterLayout>

    外层的OutterLayout:

    public class OutterLayout extends LinearLayout {
    
        private final String TAG = "OutterLayout";
    
        public OutterLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            Log.d(TAG, "dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            int action = ev.getAction();
    
            switch (action) {
    
            case MotionEvent.ACTION_DOWN:
    
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_DOWN");
    
                break;
    
            case MotionEvent.ACTION_MOVE:
    
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_MOVE");
    
                break;
    
            case MotionEvent.ACTION_UP:
    
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_UP");
    
                break;
    
            case MotionEvent.ACTION_CANCEL:
    
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_CANCEL");
    
                break;
            }
            return false;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            int action = event.getAction();
    
            switch (action) {
    
            case MotionEvent.ACTION_DOWN:
    
                Log.d(TAG, "onTouchEvent action:ACTION_DOWN");
    
                break;
    
            case MotionEvent.ACTION_MOVE:
    
                Log.d(TAG, "onTouchEvent action:ACTION_MOVE");
    
                break;
    
            case MotionEvent.ACTION_UP:
    
                Log.d(TAG, "onTouchEvent action:ACTION_UP");
    
                break;
    
            case MotionEvent.ACTION_CANCEL:
    
                Log.d(TAG, "onTouchEvent action:ACTION_CANCEL");
    
                break;
    
            }
    
            return true;
        }
    
    }

    中间层的InnerLayout

    public class InnerLayout extends LinearLayout{
    
        private final String TAG = "InnerLayout";
        public InnerLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int action = ev.getAction();
    
               switch(action){
    
               case MotionEvent.ACTION_DOWN:
    
                   Log.d(TAG,"onInterceptTouchEvent action:ACTION_DOWN");
    
                   break;
    
               case MotionEvent.ACTION_MOVE:
    
                   Log.d(TAG,"onInterceptTouchEvent action:ACTION_MOVE");
    
                   break;
    
               case MotionEvent.ACTION_UP:
    
                   Log.d(TAG,"onInterceptTouchEvent action:ACTION_UP");
    
                   break;
    
               case MotionEvent.ACTION_CANCEL:
    
                   Log.d(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");
    
                   break;
    
               }
    
              
    
               return false;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            
            int action = event.getAction();
    
               switch(action){
    
               case MotionEvent.ACTION_DOWN:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_DOWN");
    
                   break;
    
               case MotionEvent.ACTION_MOVE:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_MOVE");
    
                   break;
    
               case MotionEvent.ACTION_UP:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_UP");
    
                   break;
    
               case MotionEvent.ACTION_CANCEL:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");
    
                   break;
    
               }
               return false;
        }
        
    }

    内层的TextView:

    public class MyTextView extends TextView{
    
        private final String TAG = "MyTextView";
        public MyTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            
            int action = event.getAction();
    
               switch(action){
    
               case MotionEvent.ACTION_DOWN:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_DOWN");
    
                   break;
    
               case MotionEvent.ACTION_MOVE:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_MOVE");
    
                   break;
    
               case MotionEvent.ACTION_UP:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_UP");
    
                   break;
    
               case MotionEvent.ACTION_CANCEL:
    
                   Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");
    
                   break;
    
               }
               return true;
        }
    }

    MainActivity什么也没写,就不贴出来了。

    android的事件传递,就是dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent这三个家伙纠缠不清,今天就来看看它们的关系。

    1.dispatchTouchEvent

    首先先说下dispatchTouchEvent,字面意思就是分发Touch事件。我最早看的关于Touch事件传递的一篇博文中(http://mobile.51cto.com/abased-374715.htm),这样写道:

      当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View, TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由  dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。

    我表示红色部分对我误导很大啊。来看看源码:

      1     @Override
      2     public boolean dispatchTouchEvent(MotionEvent ev) {
      3         if (mInputEventConsistencyVerifier != null) {
      4             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
      5         }
      6 
      7         boolean handled = false;
      8         if (onFilterTouchEventForSecurity(ev)) {
      9             final int action = ev.getAction();
     10             final int actionMasked = action & MotionEvent.ACTION_MASK;
     11 
     12             // Handle an initial down.
     13             if (actionMasked == MotionEvent.ACTION_DOWN) {
     14                 // Throw away all previous state when starting a new touch gesture.
     15                 // The framework may have dropped the up or cancel event for the previous gesture
     16                 // due to an app switch, ANR, or some other state change.
     17                 cancelAndClearTouchTargets(ev);
     18                 resetTouchState();
     19             }
     20 
     21             // Check for interception.
     22             final boolean intercepted;
     23             if (actionMasked == MotionEvent.ACTION_DOWN
     24                     || mFirstTouchTarget != null) {
     25                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
     26                 if (!disallowIntercept) {
     27                     intercepted = onInterceptTouchEvent(ev);
     28                     ev.setAction(action); // restore action in case it was changed
     29                 } else {
     30                     intercepted = false;
     31                 }
     32             } else {
     33                 // There are no touch targets and this action is not an initial down
     34                 // so this view group continues to intercept touches.
     35                 intercepted = true;
     36             }
     37 
     38             // Check for cancelation.
     39             final boolean canceled = resetCancelNextUpFlag(this)
     40                     || actionMasked == MotionEvent.ACTION_CANCEL;
     41 
     42             // Update list of touch targets for pointer down, if needed.
     43             final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
     44             TouchTarget newTouchTarget = null;
     45             boolean alreadyDispatchedToNewTouchTarget = false;
     46             if (!canceled && !intercepted) {
     47                 if (actionMasked == MotionEvent.ACTION_DOWN
     48                         || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
     49                         || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
     50                     final int actionIndex = ev.getActionIndex(); // always 0 for down
     51                     final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
     52                             : TouchTarget.ALL_POINTER_IDS;
     53 
     54                     // Clean up earlier touch targets for this pointer id in case they
     55                     // have become out of sync.
     56                     removePointersFromTouchTargets(idBitsToAssign);
     57 
     58                     final int childrenCount = mChildrenCount;
     59                     if (childrenCount != 0) {
     60                         // Find a child that can receive the event.
     61                         // Scan children from front to back.
     62                         final View[] children = mChildren;
     63                         final float x = ev.getX(actionIndex);
     64                         final float y = ev.getY(actionIndex);
     65 
     66                         final boolean customOrder = isChildrenDrawingOrderEnabled();
     67                         for (int i = childrenCount - 1; i >= 0; i--) {
     68                             final int childIndex = customOrder ?
     69                                     getChildDrawingOrder(childrenCount, i) : i;
     70                             final View child = children[childIndex];
     71                             if (!canViewReceivePointerEvents(child)
     72                                     || !isTransformedTouchPointInView(x, y, child, null)) {
     73                                 continue;
     74                             }
     75 
     76                             newTouchTarget = getTouchTarget(child);
     77                             if (newTouchTarget != null) {
     78                                 // Child is already receiving touch within its bounds.
     79                                 // Give it the new pointer in addition to the ones it is handling.
     80                                 newTouchTarget.pointerIdBits |= idBitsToAssign;
     81                                 break;
     82                             }
     83 
     84                             resetCancelNextUpFlag(child);
     85                             if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
     86                                 // Child wants to receive touch within its bounds.
     87                                 mLastTouchDownTime = ev.getDownTime();
     88                                 mLastTouchDownIndex = childIndex;
     89                                 mLastTouchDownX = ev.getX();
     90                                 mLastTouchDownY = ev.getY();
     91                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);
     92                                 alreadyDispatchedToNewTouchTarget = true;
     93                                 break;
     94                             }
     95                         }
     96                     }
     97 
     98                     if (newTouchTarget == null && mFirstTouchTarget != null) {
     99                         // Did not find a child to receive the event.
    100                         // Assign the pointer to the least recently added target.
    101                         newTouchTarget = mFirstTouchTarget;
    102                         while (newTouchTarget.next != null) {
    103                             newTouchTarget = newTouchTarget.next;
    104                         }
    105                         newTouchTarget.pointerIdBits |= idBitsToAssign;
    106                     }
    107                 }
    108             }
    109 
    110             // Dispatch to touch targets.
    111             if (mFirstTouchTarget == null) {
    112                 // No touch targets so treat this as an ordinary view.
    113                 handled = dispatchTransformedTouchEvent(ev, canceled, null,
    114                         TouchTarget.ALL_POINTER_IDS);
    115             } else {
    116                 // Dispatch to touch targets, excluding the new touch target if we already
    117                 // dispatched to it.  Cancel touch targets if necessary.
    118                 TouchTarget predecessor = null;
    119                 TouchTarget target = mFirstTouchTarget;
    120                 while (target != null) {
    121                     final TouchTarget next = target.next;
    122                     if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
    123                         handled = true;
    124                     } else {
    125                         final boolean cancelChild = resetCancelNextUpFlag(target.child)
    126                         || intercepted;
    127                         if (dispatchTransformedTouchEvent(ev, cancelChild,
    128                                 target.child, target.pointerIdBits)) {
    129                             handled = true;
    130                         }
    131                         if (cancelChild) {
    132                             if (predecessor == null) {
    133                                 mFirstTouchTarget = next;
    134                             } else {
    135                                 predecessor.next = next;
    136                             }
    137                             target.recycle();
    138                             target = next;
    139                             continue;
    140                         }
    141                     }
    142                     predecessor = target;
    143                     target = next;
    144                 }
    145             }
    146 
    147             // Update list of touch targets for pointer up or cancel, if needed.
    148             if (canceled
    149                     || actionMasked == MotionEvent.ACTION_UP
    150                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
    151                 resetTouchState();
    152             } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
    153                 final int actionIndex = ev.getActionIndex();
    154                 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
    155                 removePointersFromTouchTargets(idBitsToRemove);
    156             }
    157         }
    158 
    159         if (!handled && mInputEventConsistencyVerifier != null) {
    160             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
    161         }
    162         return handled;
    163     }

    略长,不用都看明白了主要有以下几个关键点:

      1) 21行的注释在说:检查是否拦截;

      2) 27行调用了onInterceptTouchEvent()方法,并把其返回结果赋给了Intercepted变量;

      3) 46行再次看到了Intercepted变量,作为if()语句的判断条件。

    也就是说,dispatchTouchEvent()方法中调用了onInterceptTouchEvent()方法,而且将其返回值保存,此时dispatchTouchEvent()还没有返回,所以并不是根据dispatchTouchEvent()方法的返回值来决定事件下一步怎么走。在本例中,如果在OutterLayout中重写dispatchTouchEvent()方法,直接返回true或false,而不是return super.dispatchTouchEvent(ev)的话,则根本没有调用onInterceptTouchEvent()方法,事件根本不会传递到InnerLayout和MyTextView中去,而且也没有触发OutterLayout的onTouchEvent()方法。在实际开发中,不建议重写dispatchTouchEvent()方法,就算重写也应该return super.dispatchTouchEvent(ev)。

    2.onInterceptTouchEvent()

    参照http://blog.csdn.net/ddna/article/details/5473293,基本的规则是:

    1. down事件首先会传递到onInterceptTouchEvent()方法
    2. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
    3. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
    4. 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
    5. 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

    首先onInterceptTouchEvent()是ViewGroup的方法,View是没有该方法的。再来看onInterceptTouchEvent()源码:

        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }

    默认返回false。那么修改测试程序(把dispatchTouchEvent()方法注释掉):

      1)让OutterLayout的onInterceptTouchEvent()返回true(整体返回true或者ACTION_DOWN返回true是一样的),onTouchEvent()返回true,然后看Log:

      2)让OutterLayout的onInterceptTouchEvent()返回false,onTouchEvent()返回true,InnerLayout的onInterceptTouchEvent()返回true,onTouchEvent()返回false,然后看Log:

      3)让OutterLayout的onInterceptTouchEvent()返回false,onTouchEvent()返回true,InnerLayout的onInterceptTouchEvent()返回true,onTouchEvent()返回true,然后看Log:

      4)让OutterLayout的onInterceptTouchEvent()返回false,onTouchEvent()返回true,InnerLayout的onInterceptTouchEvent()返回false,onTouchEvent()返回true,MyTextView的onTouchEvent()返回true,然后看Log:

    我的理解是:OutterLayout的onInterceptTouchEvent()先收到Touch事件:

      1.如果OutterLayout的onInterceptTouchEvent()返回true,表示拦截这次事件,则事件不再传递给InnerLayout和MyTextView,而是交由OutterLayout的onTouchEvent()处理:

        1.1如果OutterLayout的onTouchEvent()返回true,表示事件被消费了,事件传播到此结束;

        1.2如果OutterLayout的onTouchEvent()返回true,但因为OutterLayout是顶层布局,所以无法继续向上传播,事件就此消失。

      2.如果OutterLayout的onInterceptTouchEvent()返回false,表示不拦截,事件将传递给InnerLayout的onInterceptTouchEvent()处理:

        2.1如果InnerLayout的onInterceptTouchEvent()返回true,表示拦截这次事件,则事件不再传递给MyTextView,而是交由InnerLayout的onTouchEvent()处理:

          2.1.1如果InnerLayout的onTouchEvent()返回true,表示事件被消费了,事件传播到此结束;

          2.1.2如果InnerLayout的onTouchEvent()返回false,则事件继续传递到OutterLayout的onTouchEvent()处理——跳到1.1;

        2.2如果InnerLayout的onInterceptTouchEvent()返回false,表示不拦截,则事件将传递给MyTextView的onTouchEvent()处理:

          2.2.1如果MyTextView的onTouchEvent()返回true,表示事件被消费了,事件传播到此结束;

          2.2.2如果MyTextView的onTouchEvent()返回false,则事件继续传递到InnerLayout的onTouchEvent()处理——跳到2.1.1;

  • 相关阅读:
    介绍一种很好用的任务调度平台
    java中的进制与操作符
    类再生(合成、继承、final)
    初始化
    重新学习Spring2——IOC和AOP原理彻底搞懂
    重新学习Spring一--Spring在web项目中的启动过程
    JDK并发包
    java并行程序基础
    MVC模式
    访问者模式
  • 原文地址:https://www.cnblogs.com/Couch-potato/p/3811963.html
Copyright © 2011-2022 走看看