zoukankan      html  css  js  c++  java
  • android事件学习

    一、android处理事件有两种形式.

      1、基于监听的事件处理,就是通过setXxxListenter()进行处理的。

      2、基于回调的事件处理,就是View类内部的onTouchEvent(),一般是在自定义控件时重写的。

    关于这些方法是在什么时候被触发的,下面是对部分源码的分析:

      1、首先:触摸事件会触发Activity的dispatchTouchEvent,

     1 public boolean dispatchTouchEvent(MotionEvent ev) {
     2     // onUserInteraction默认不执行任何动作。
     3     // 它是提供给使用者的接口。
     4     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
     5         onUserInteraction();
     6     }
     7     // 这里会调用到ViewGroup的dispatchTouchEvent(),
     8     // 即会调用Activity包含的根视图的dispatchTouchEvent()。
     9     if (getWindow().superDispatchTouchEvent(ev)) {
    10         return true;
    11     }
    12     // 如果superDispatchTouchEvent()返回false,
    13     // 即Activity的根视图以及根视图的子视图都没有拦截该事件的话,则调用Activity的onTouchEvent()
    14     return onTouchEvent(ev);
    15 }
    Activity的dispatchTouchEvent

        对于getWindow().superDispatchTouchEvent(ev)一句追踪源码可以发现()这里调用了DecorView的superDispatchTouchEvent(),而DecorView是PhoneWindow的内部类,并且是Window界面的顶级View

    1 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    2     ...
    3 
    4     public boolean superDispatchTouchEvent(MotionEvent event) {
    5         return super.dispatchTouchEvent(event);
    6     }
    7 
    8     ...
    9 }
    DecorView

        从上面的DecorView源码部分可以看到:通过DecorView,Activity的触摸事件将转发给GroupView的dispatchTouchEvent()进行处理。

        在Activity的dispatchTouchEvent()中,如果GroupView的dispatchTouchEvent()返回true则会拦截消耗事件,否则将执行Activity的onTouchEvent。

     1 public boolean onTouchEvent(MotionEvent event) {
     2     if (mWindow.shouldCloseOnTouch(this, event)) {
     3         finish();
     4         return true;
     5     }    
     6 
     7     return false;
     8 }  
     9 
    10 //上面会调用Window的shouldCloseOnTouch()
    11 public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
    12     if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
    13             && isOutOfBounds(context, event) && peekDecorView() != null) {
    14         return true;
    15     }    
    16     return false;
    17 }   
    onTouchEvent

        上面可以看到:这个方法是对ctivity自身触摸事件的默认处理,可以用于关闭Dialog主题的Activity

        !!!详询参见http://wangkuiwu.github.io/2015/01/02/TouchEvent-Activity/

       2、接着:由GroupView的dispatchTouchEvent()继续进行事件的分发

      1 public boolean dispatchTouchEvent(MotionEvent ev) {
      2     // mInputEventConsistencyVerifier是调试用的,不会理会
      3     if (mInputEventConsistencyVerifier != null) {
      4         mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
      5     }
      6 
      7     // 第1步:是否要分发该触摸事件
      8     //
      9     // onFilterTouchEventForSecurity()表示是否要分发该触摸事件。 
     10     // 如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即返回false。
     11     // 否则,则对触摸事件进行分发,即返回true。
     12     boolean handled = false;
     13     if (onFilterTouchEventForSecurity(ev)) {
     14         final int action = ev.getAction();
     15         final int actionMasked = action & MotionEvent.ACTION_MASK;
     16 
     17         // 第2步:检测是否需要清空目标和状态
     18         //
     19         // 如果是ACTION_DOWN(即按下事件),则清空之前的触摸事件处理目标和状态。
     20         // 这里的情况状态包括:
     21         // (01) 清空mFirstTouchTarget链表,并设置mFirstTouchTarget为null。
     22         //      mFirstTouchTarget是"接受触摸事件的View"所组成的单链表
     23         // (02) 清空mGroupFlags的FLAG_DISALLOW_INTERCEPT标记
     24         //      如果设置了FLAG_DISALLOW_INTERCEPT,则不允许ViewGroup对触摸事件进行拦截。
     25         // (03) 清空mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVEN标记
     26         if (actionMasked == MotionEvent.ACTION_DOWN) {
     27             cancelAndClearTouchTargets(ev);
     28             resetTouchState();
     29         }    
     30 
     31         // 第3步:检查当前ViewGroup是否想要拦截触摸事件
     32         // 
     33         // 是的话,设置intercepted为true;否则intercepted为false。
     34         // 如果是"按下事件(ACTION_DOWN)" 或者 mFirstTouchTarget不为null;就执行if代码块里面的内容。
     35         // 否则的话,设置intercepted为true。
     36         final boolean intercepted;
     37         if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
     38             // 检查禁止拦截标记:FLAG_DISALLOW_INTERCEPT
     39             // 如果调用了requestDisallowInterceptTouchEvent()标记的话,则FLAG_DISALLOW_INTERCEPT会为true。
     40             // 例如,ViewPager在处理触摸事件的时候,就会调用requestDisallowInterceptTouchEvent()
     41             //     ,禁止它的父类对触摸事件进行拦截
     42             final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
     43             if (!disallowIntercept) {
     44                 // 如果禁止拦截标记为false的话,则调用onInterceptTouchEvent();并返回拦截状态。
     45                 intercepted = onInterceptTouchEvent(ev);
     46                 ev.setAction(action); // restore action in case it was changed
     47             } else {
     48                 intercepted = false;
     49             }    
     50         } else {
     51             intercepted = true;
     52         }    
     53 
     54         // 第4步:检查当前的触摸事件是否被取消
     55         // 
     56         // (01) 对于ACTION_DOWN而言,mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVENT位肯定是0;因此,canceled=false。
     57         // (02) 当前的View或ViewGroup要被从父View中detach时,PFLAG_CANCEL_NEXT_UP_EVENT就会被设为true;
     58         //      此时,它就不再接受触摸事情。
     59         final boolean canceled = resetCancelNextUpFlag(this)
     60                 || actionMasked == MotionEvent.ACTION_CANCEL;
     61 
     62         // 第5步:将触摸事件分发给"当前ViewGroup的子View和子ViewGroup"
     63         // 
     64         // 如果触摸"没有被取消",同时也"没有被拦截"的话,则将触摸事件分发给它的子View和子ViewGroup。  
     65         //     如果当前ViewGroup的孩子有接受触摸事件的话,则将该孩子添加到mFirstTouchTarget链表中。
     66         final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
     67         TouchTarget newTouchTarget = null;
     68         boolean alreadyDispatchedToNewTouchTarget = false;
     69         if (!canceled && !intercepted) {
     70             if (actionMasked == MotionEvent.ACTION_DOWN
     71                     || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
     72                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
     73                 // 这是获取触摸事件的序号 以及 触摸事件的id信息。
     74                 // (01) 对于ACTION_DOWN,actionIndex肯定是0
     75                 // (02) 而getPointerId()是获取的该触摸事件的id,并将该id信息保存到idBitsToAssign中。
     76                 //    这个触摸事件的id是为多指触摸而添加的;对于单指触摸,getActionIndex()返回的肯定是0;
     77                 //    而对于多指触摸,第一个手指的id是0,第二个手指的id是1,第三个手指的id是2,...依次类推。
     78                 final int actionIndex = ev.getActionIndex();
     79                 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
     80                         : TouchTarget.ALL_POINTER_IDS;
     81 
     82                 // 清空这个手指之前的TouchTarget链表。
     83                 // 一个TouchTarget,相当于一个可以被触摸的对象;它中记录了接受触摸事件的View
     84                 removePointersFromTouchTargets(idBitsToAssign);
     85 
     86                 // 获取该ViewGroup包含的View和ViewGroup的数目,
     87                 // 然后递归遍历ViewGroup的孩子,对触摸事件进行分发。
     88                 // 递归遍历ViewGroup的孩子:是指对于当前ViewGroup的所有孩子,都会逐个遍历,并分发触摸事件;
     89                 //   对于逐个遍历到的每一个孩子,若该孩子是ViewGroup类型的话,则会递归到调用该孩子的孩子,...
     90                 final int childrenCount = mChildrenCount;
     91                 if (newTouchTarget == null && childrenCount != 0) {
     92                     final float x = ev.getX(actionIndex);
     93                     final float y = ev.getY(actionIndex);
     94                     final View[] children = mChildren;
     95 
     96                     final boolean customOrder = isChildrenDrawingOrderEnabled();
     97                     for (int i = childrenCount - 1; i >= 0; i--) {
     98                         final int childIndex = customOrder ?
     99                                 getChildDrawingOrder(childrenCount, i) : i;
    100                         final View child = children[childIndex];
    101                         // 如果child可以接受触摸事件,
    102                         // 并且触摸坐标(x,y)在child的可视范围之内的话;
    103                         // 则继续往下执行。否则,调用continue。
    104                         // child可接受触摸事件:是指child的是可见的(VISIBLE);或者虽然不可见,但是位于动画状态。
    105                         if (!canViewReceivePointerEvents(child)
    106                                 || !isTransformedTouchPointInView(x, y, child, null)) {
    107                             continue;
    108                         }
    109 
    110                         // getTouchTarget()的作用是查找child是否存在于mFirstTouchTarget的单链表中。
    111                         // 是的话,返回对应的TouchTarget对象;否则,返回null。
    112                         newTouchTarget = getTouchTarget(child);
    113                         if (newTouchTarget != null) {
    114                             newTouchTarget.pointerIdBits |= idBitsToAssign;
    115                             break;
    116                         }
    117 
    118                         // 重置child的mPrivateFlags变量中的PFLAG_CANCEL_NEXT_UP_EVENT位。
    119                         resetCancelNextUpFlag(child);
    120 
    121                         // 调用dispatchTransformedTouchEvent()将触摸事件分发给child。
    122                         if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
    123                             // 如果child能够接受该触摸事件,即child消费或者拦截了该触摸事件的话;
    124                             // 则调用addTouchTarget()将child添加到mFirstTouchTarget链表的表头,并返回表头对应的TouchTarget
    125                             // 同时还设置alreadyDispatchedToNewTouchTarget为true。
    126                             mLastTouchDownTime = ev.getDownTime();
    127                             mLastTouchDownIndex = childIndex;
    128                             mLastTouchDownX = ev.getX();
    129                             mLastTouchDownY = ev.getY();
    130                             newTouchTarget = addTouchTarget(child, idBitsToAssign);
    131                             alreadyDispatchedToNewTouchTarget = true;
    132                             break;
    133                         }
    134                     }
    135                 }
    136 
    137                 // 如果newTouchTarget为null,并且mFirstTouchTarget不为null;
    138                 // 则设置newTouchTarget为mFirstTouchTarget链表中第一个不为空的节点。
    139                 if (newTouchTarget == null && mFirstTouchTarget != null) {
    140                     // Did not find a child to receive the event.
    141                     // Assign the pointer to the least recently added target.
    142                     newTouchTarget = mFirstTouchTarget;
    143                     while (newTouchTarget.next != null) {
    144                         newTouchTarget = newTouchTarget.next;
    145                     }
    146                     newTouchTarget.pointerIdBits |= idBitsToAssign;
    147                 }
    148             }
    149         }
    150 
    151         // 第6步:进一步的对触摸事件进行分发
    152         // 
    153         // (01) 如果mFirstTouchTarget为null,意味着还没有任何View来接受该触摸事件;
    154         //   此时,将当前ViewGroup看作一个View;
    155         //   将会调用"当前的ViewGroup的父类View的dispatchTouchEvent()"对触摸事件进行分发处理。
    156         //   即,会将触摸事件交给当前ViewGroup的onTouch(), onTouchEvent()进行处理。
    157         // (02) 如果mFirstTouchTarget不为null,意味着有ViewGroup的子View或子ViewGroup中,
    158         //   有可以接受触摸事件的。那么,就将触摸事件分发给这些可以接受触摸事件的子View或子ViewGroup。
    159         if (mFirstTouchTarget == null) {
    160             // 注意:这里的第3个参数是null
    161             handled = dispatchTransformedTouchEvent(ev, canceled, null,
    162                     TouchTarget.ALL_POINTER_IDS);
    163         } else {
    164             // Dispatch to touch targets, excluding the new touch target if we already
    165             // dispatched to it.  Cancel touch targets if necessary.
    166             TouchTarget predecessor = null;
    167             TouchTarget target = mFirstTouchTarget;
    168             while (target != null) {
    169                 final TouchTarget next = target.next;
    170                 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
    171                     handled = true;
    172                 } else {
    173                     final boolean cancelChild = resetCancelNextUpFlag(target.child)
    174                             || intercepted;
    175                     if (dispatchTransformedTouchEvent(ev, cancelChild,
    176                             target.child, target.pointerIdBits)) {
    177                         handled = true;
    178                     }
    179                     if (cancelChild) {
    180                         if (predecessor == null) {
    181                             mFirstTouchTarget = next;
    182                         } else {
    183                             predecessor.next = next;
    184                         }
    185                         target.recycle();
    186                         target = next;
    187                         continue;
    188                     }
    189                 }
    190                 predecessor = target;
    191                 target = next;
    192             }
    193         }
    194 
    195         // 第7步:再次检查取消标记,并进行相应的处理
    196         // 
    197         // Update list of touch targets for pointer up or cancel, if needed.
    198         if (canceled
    199                 || actionMasked == MotionEvent.ACTION_UP
    200                 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
    201             resetTouchState();
    202         } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
    203             final int actionIndex = ev.getActionIndex();
    204             final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
    205             removePointersFromTouchTargets(idBitsToRemove);
    206         }
    207     }
    208 
    209     // mInputEventConsistencyVerifier是调试用的,不会理会
    210     if (!handled && mInputEventConsistencyVerifier != null) {
    211         mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
    212     }
    213     return handled;
    214 }
    GroupView的dispatchTouchEvent

        上面还需注意的就是第5步:在分发事件时,除了判断是否被拦截或取消后,内部又判断了是否是Down事件,也就是只有在Down事件没有被拦截或取消,才会向其分发事件

    参考:http://wangkuiwu.github.io/2015/01/04/TouchEvent-ViewGroup/

        3、最后,触摸事件会触发view中的dispatchTouchEvent(),如果没有子类中没有重写,则会向上寻找一个执行。下面是View中的部分源码,就是具体执行事件的部分

    1  ListenerInfo li = mListenerInfo;
    2 if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {
    3           result = true;
    4  }
    5 
    6  if (!result && onTouchEvent(event)) {
    7          result = true;
    8  } 
    顶级View的dispatchTouchEvent

      上面是23版本的片段,这部分代码写的比较巧妙,第一个if中:

        1、li != null,这是ListenerInfo对象,保存各种监听器实例的对象,只要有一个监听器被设置,则该对象不为空。

     1     static class ListenerInfo {
     2         protected OnFocusChangeListener mOnFocusChangeListener;
     3 
     4         private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
     5 
     6         protected OnScrollChangeListener mOnScrollChangeListener;
     7 
     8         private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
     9 
    10         public OnClickListener mOnClickListener;
    11 
    12         protected OnLongClickListener mOnLongClickListener;
    13 
    14         protected OnContextClickListener mOnContextClickListener;
    15 
    16         protected OnCreateContextMenuListener mOnCreateContextMenuListener;
    17 
    18         private OnKeyListener mOnKeyListener;
    19 
    20         private OnTouchListener mOnTouchListener;
    21 
    22         private OnHoverListener mOnHoverListener;
    23 
    24         private OnGenericMotionListener mOnGenericMotionListener;
    25 
    26         private OnDragListener mOnDragListener;
    27 
    28         private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
    29 
    30         OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
    31     }
    32 
    33     ListenerInfo mListenerInfo;
    34 
    35     public void setOnTouchListener(OnTouchListener l) {
    36         //在设置监听器实例时,这里会调用getListenerInfo()判断是否有ListenerInfo实例存在
    37         getListenerInfo().mOnTouchListener = l;
    38     }
    39 
    40     ListenerInfo getListenerInfo() {
    41         if (mListenerInfo != null) {
    42             return mListenerInfo;
    43         }
    44         mListenerInfo = new ListenerInfo();
    45         return mListenerInfo;
    46     }
    mListenerInfo部分

        2、li.mOnTouchListener != null,这是判断是否有TouchListener,这是通过setOnTouchListener()设置的。

        3、(mViewFlags & ENABLED_MASK) == ENABLED,这是判断View的mViewFlags是否是ENABLED。注意:这和View是否可点击不同,可点击是CLICKABLE=true

     1     //检测是否是ENABLED,获取和设置都与mViewFlags有关
     2     public boolean isEnabled() {
     3         return (mViewFlags & ENABLED_MASK) == ENABLED;
     4     }
     5 
     6     public void setEnabled(boolean enabled) {
     7         if (enabled == isEnabled()) return;
     8 
     9         setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);
    10 
    11 
    12     //下面是与点击有关的获取和设置
    13     public boolean isClickable() {
    14         return (mViewFlags & CLICKABLE) == CLICKABLE;
    15     }
    16 
    17     public void setClickable(boolean clickable) {
    18         setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
    19     }
    20 
    21     public boolean isLongClickable() {
    22         return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
    23     }
    24 
    25     public void setLongClickable(boolean longClickable) {
    26         setFlags(longClickable ? LONG_CLICKABLE : 0, LONG_CLICKABLE);
    27     }
    28 
    29     public boolean isContextClickable() {
    30         return (mViewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    31     }
    32 
    33     public void setContextClickable(boolean contextClickable) {
    34         setFlags(contextClickable ? CONTEXT_CLICKABLE : 0, CONTEXT_CLICKABLE);
    35     }
    36 
    37     //注意
    38     public void setOnClickListener(@Nullable OnClickListener l) {
    39         if (!isClickable()) {
    40             setClickable(true);
    41         }
    42         getListenerInfo().mOnClickListener = l;
    43     }
    Enable和Clickable的区别

        !!!注意:在setOnClickListener中会修改Clickable属性,即原本不能点击的View,如果添加点击的监听器,则可以点击。

        4、li.mOnTouchListener.onTouch(this, event),就是判断添加的监听事件额返回值。

        !!!只有当上面四个都是true时,设置result=true。而这个值又影响到下一个判断的执行,因为用到&&(这个操作符比较特殊,如果前一个操作数为true,则继续向后判断;如果前一个为false,则不执行后续判断直接返回false),所以前面是false这直接退出执行,否则会触发View的onTouchEvent(event)。该方法比较重要的两步见下面说明。需要注意的是:这个方法的返回值为true,则事件被消耗,否则将继续!!向后(其他的点击事件)也包括向!!父布局传递

     1 public boolean onTouchEvent(MotionEvent event) {
     2     final int viewFlags = mViewFlags;
     3 
     4     // 如果View被禁用的话,则返回它是否可以点击。
     5     if ((viewFlags & ENABLED_MASK) == DISABLED) {
     6         if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
     7             setPressed(false);
     8         }
     9         return (((viewFlags & CLICKABLE) == CLICKABLE ||
    10                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
    11     }
    12 
    13     // 如果该View的mTouchDelegate不为null的话,将触摸消息分发给mTouchDelegate。
    14     // mTouchDelegate的默认值是null。
    15     if (mTouchDelegate != null) {
    16         if (mTouchDelegate.onTouchEvent(event)) {
    17             return true;
    18         }
    19     }
    20 
    21     // 如果View可以被点击的话,则执行if里面的内容。
    22     // 这其中涉及到的主要是获取焦点,设置按下状态,触发onClick(), onLongClick()事件等等。
    23     if (((viewFlags & CLICKABLE) == CLICKABLE ||
    24             (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
    25                。。。
    26         }
    27         return true;
    28     }
    29 
    30     return false;
    31 }    
    View的onTouchEvent

        这个方法的返回值总结:

          a、该View不可用,即(viewFlags & ENABLED_MASK) == DISABLED,则返回是否可点击或长击。如果可用则向下判断

          b、mTouchDelegate如果不为null,则由其进行处理并返回结果,否则继续向下判断

          c、到这这一步,只要View可以点击或长击则返回true,否则返回false。

     下图是View事件分发的全过程图示,重点是事件首先是由顶级布局获取的,而不是其中的子View.

        

     1 package com.dqxst.first.view;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.util.Log;
     6 import android.view.MotionEvent;
     7 import android.view.View;
     8 import android.view.View.OnClickListener;
     9 import android.view.View.OnTouchListener;
    10 import android.widget.Button;
    11 import android.widget.ImageView;
    12 import android.widget.LinearLayout;
    13 
    14 import com.dqxst.first.R;
    15 import com.dqxst.first.myui.MyBtn;
    16 
    17 public class EventActivity extends Activity{
    18     private final static String TAG="EventActivity";
    19     private LinearLayout parent;
    20     private Button btn;
    21     private MyBtn myBtn;
    22     private ImageView image;
    23     
    24     @Override
    25     protected void onCreate(Bundle savedInstanceState) {
    26         super.onCreate(savedInstanceState);
    27         setContentView(R.layout.activity_event);
    28         findView();
    29         event();
    30         Log.i(TAG, "enabled="+image.isEnabled());
    31         Log.i(TAG, "LongClickable="+image.isLongClickable());
    32     }
    33 
    34     private void event() {
    35         parent.setOnClickListener(new OnClickListener() {
    36             @Override
    37             public void onClick(View v) {
    38                 Log.i(TAG, "父布局的OnClickListener被触发。。。");
    39             }
    40         });
    41         btn.setOnClickListener(new OnClickListener() {
    42             @Override
    43             public void onClick(View v) {
    44                 Log.i(TAG, "点击btn,OnClickListener。。。");
    45             }
    46         });
    47         btn.setOnTouchListener(new OnTouchListener() {
    48             @Override
    49             public boolean onTouch(View v, MotionEvent event) {
    50                 Log.i(TAG, "点击btn,OnTouchListener。。。,事件action="+event.getAction());
    51                 return false;
    52             }
    53         });
    54 
    55         myBtn.setOnClickListener(new OnClickListener() {
    56             @Override
    57             public void onClick(View v) {
    58                 Log.i(TAG, "点击myBtn,OnClickListener。。。");
    59             }
    60         });
    61         myBtn.setOnTouchListener(new OnTouchListener() {
    62             @Override
    63             public boolean onTouch(View v, MotionEvent event) {
    64                 Log.i(TAG, "点击myBtn,OnTouchListener。。。,事件action="+event.getAction());
    65                 return false;
    66             }
    67         });
    68         image.setOnClickListener(new OnClickListener() {
    69             @Override
    70             public void onClick(View v) {
    71                 Log.i(TAG, "点击image,OnClickListener。。。");
    72             }
    73         });
    74         image.setOnTouchListener(new OnTouchListener() {
    75             @Override
    76             public boolean onTouch(View v, MotionEvent event) {
    77                 Log.i(TAG, "点击image,OnTouchListener。。。,事件action="+event.getAction());
    78                 return false;
    79             }
    80         });
    81     }
    82     
    83     //上面已经有setOnClickListener,所以该方法不执行
    84     public void btn_click(View view){
    85         Log.i(TAG, "点击btn,通过xml绑定的onclick事件处理");
    86     }
    87 
    88     private void findView() {
    89         parent=(LinearLayout) findViewById(R.id.parent);
    90         btn=(Button) findViewById(R.id.btn);
    91         myBtn=(MyBtn) findViewById(R.id.mybtn);
    92         image=(ImageView) findViewById(R.id.event_image);
    93     }
    94 }
    事件处理的例子

    参考:http://blog.csdn.net/guolin_blog/article/details/9097463http://blog.csdn.net/guolin_blog/article/details/9153747

    二、MotionEvent触摸事件,这是上面事件分发之后最终在OnTouchListener()执行的事件对象。通过这个对象可以获取当前的触摸事件。

      1、单手操作:通过调用getAction()可以获取事件标识,可以和MotionEvent中定义的事件标识进行匹配。

      2、多手操作:如果需要判断多手操作需要event.getAction() & MotionEvent.ACTION_MASK来获取事件。

    在MotionEvent对象中最重要的属性就是触发事件的X,Y坐标。通过getX()/getY()获取。多点触控通过getX(int pointerIndex) ,来获得对应手指事件的发生位置. 获得Y轴用getY(int pointerIndex)

    参考:http://www.runoob.com/w3cnote/android-tutorial-touchlistener-ontouchevent.html

    http://my.oschina.net/banxi/blog/56421

    三、手势事件:其实就是android提供的封装了对MotionEvent事件的处理,把一些连续的操作响应为手势(当然也可以手动处理,但是比较麻烦)。主要用到的是GestureDetector类,使用过程分三步:

      1、创建手势监听器对象,这是通过集成/实现该类中的接口/实现类来完成的,比如OnGestureListener接口/SimpleOnGestureListener类。

      2、创建GestureDetector对象,需要通过构造器传入上面的手势监听器对象。

      3、将事件转交给该对象,通过GestureDetector.onTouch()进行事件的处理。

    需要注意的是,手势事件可以针对整个activity或者其中一个View。

      1、如果针对Activity,那就不需要为其中的View添加事件监听,直接重写onTouchEvent()将事件转发即可。

      2、如果是针对特定View,那就需要给其添加OnTouchListener监听器,在该监听器中进行转发,注意:需要返回值必须为true才能正确响应

      1 package com.dqxst.first.view;
      2 
      3 import android.app.Activity;
      4 import android.os.Bundle;
      5 import android.util.Log;
      6 import android.view.GestureDetector;
      7 import android.view.GestureDetector.OnGestureListener;
      8 import android.view.MotionEvent;
      9 import android.view.View;
     10 import android.view.View.OnClickListener;
     11 import android.view.View.OnTouchListener;
     12 import android.widget.Button;
     13 import android.widget.ImageView;
     14 import android.widget.LinearLayout;
     15 
     16 import com.dqxst.first.R;
     17 import com.dqxst.first.myui.MyBtn;
     18 import com.dqxst.first.util.CommonUtils;
     19 
     20 public class EventActivity extends Activity implements OnTouchListener{
     21     private final static String TAG="EventActivity";
     22     private LinearLayout parent;
     23     private View view;
     24     private Button btn;
     25     private MyBtn myBtn;
     26     private ImageView image;
     27     
     28     private MyGestureListener mgListener;
     29     private GestureDetector mDetector;
     30 
     31     @Override
     32     protected void onCreate(Bundle savedInstanceState) {
     33         super.onCreate(savedInstanceState);
     34         setContentView(R.layout.activity_event);
     35         
     36         //实例化GestureListener与GestureDetector对象
     37         mgListener = new MyGestureListener();
     38         mDetector = new GestureDetector(this, mgListener);
     39         
     40         findView();
     41 
     42 //        image.setOnTouchListener(this);
     43     }
     44 
     45     private void findView() {
     46         parent=(LinearLayout) findViewById(R.id.parent);
     47         view=findViewById(R.id.event_test);
     48         btn=(Button) findViewById(R.id.btn);
     49         myBtn=(MyBtn) findViewById(R.id.mybtn);
     50         image=(ImageView) findViewById(R.id.event_image);
     51     }
     52 
     53     //如果是针对某一View,则使用下面的监听器,然后给相应的View进行set即可
     54     @Override
     55     public boolean onTouchEvent(MotionEvent event) {
     56         return mDetector.onTouchEvent(event);
     57     }
     58 
     59     @Override
     60 //    public boolean onTouch(View v, MotionEvent event) {
     61 //        mDetector.onTouchEvent(event);
     62 //        return true;
     63 //    }
     64     
     65     //自定义一个GestureListener,这个是View类下的,别写错哦!!!
     66     private class MyGestureListener implements OnGestureListener {
     67 
     68         @Override
     69         public boolean onDown(MotionEvent motionEvent) {
     70             Log.d(TAG, "onDown:按下");
     71             CommonUtils.toastShow(getApplicationContext(), "onDown:按下");
     72             return false;
     73         }
     74 
     75         @Override
     76         public void onShowPress(MotionEvent motionEvent) {
     77             Log.d(TAG, "onShowPress:手指按下一段时间,不过还没到长按");
     78             CommonUtils.toastShow(getApplicationContext(), "onShowPress:手指按下一段时间,不过还没到长按");
     79         }
     80 
     81         @Override
     82         public boolean onSingleTapUp(MotionEvent motionEvent) {
     83             Log.d(TAG, "onSingleTapUp:手指离开屏幕的一瞬间");
     84             CommonUtils.toastShow(getApplicationContext(), "onSingleTapUp:手指离开屏幕的一瞬间");
     85             return false;
     86         }
     87 
     88         @Override
     89         public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
     90             Log.d(TAG, "onScroll:在触摸屏上滑动");
     91             CommonUtils.toastShow(getApplicationContext(), "onScroll:在触摸屏上滑动");
     92             return false;
     93         }
     94 
     95         @Override
     96         public void onLongPress(MotionEvent motionEvent) {
     97             Log.d(TAG, "onLongPress:长按并且没有松开");
     98             CommonUtils.toastShow(getApplicationContext(), "onLongPress:长按并且没有松开");
     99         }
    100 
    101         @Override
    102         public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
    103             Log.d(TAG, "onFling:迅速滑动,并松开");
    104             CommonUtils.toastShow(getApplicationContext(), "onFling:迅速滑动,并松开");
    105             return false;
    106         }
    107     }
    108 
    109 }
    手势操作实例

    上面是基本使用。除此之外还可以使用GestureOverlayView编辑手势,使用GestureLibraries(手势库)来添加(刚才编辑的手势)或者获取手势。

    参考:http://www.runoob.com/w3cnote/android-tutorial-gestures.html

    package com.dqxst.first.view;

    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.GestureDetector.OnGestureListener;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.View.OnTouchListener;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.LinearLayout;

    import com.dqxst.first.R;
    import com.dqxst.first.myui.MyBtn;
    import com.dqxst.first.util.CommonUtils;

    public class EventActivity extends Activity implements OnTouchListener{
        private final static String TAG="EventActivity";
        private LinearLayout parent;
        private View view;
        private Button btn;
        private MyBtn myBtn;
        private ImageView image;
        
        private MyGestureListener mgListener;
        private GestureDetector mDetector;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_event);
            
            //实例化GestureListener与GestureDetector对象
            mgListener = new MyGestureListener();
            mDetector = new GestureDetector(this, mgListener);
            
            findView();
    //        event();
            
            Log.i(TAG, "enabled="+view.isEnabled());
            Log.i(TAG, "click="+view.isClickable());
            Log.i(TAG, "Longclick="+view.isLongClickable());
            image.setOnTouchListener(this);
        }

        private void event() {
            parent.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "父布局的OnClickListener被触发。。。");
                }
            });
            btn.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "点击btn,OnClickListener。。。");
                }
            });
            btn.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    Log.i(TAG, "点击btn,OnTouchListener。。。,事件action="+event.getAction());
                    return false;
                }
            });

            myBtn.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "点击myBtn,OnClickListener。。。");
                }
            });
            myBtn.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    Log.i(TAG, "点击myBtn,OnTouchListener。。。,事件action="+event.getAction());
                    return false;
                }
            });
            image.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "点击image,OnClickListener。。。");
                }
            });
            image.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    Log.i(TAG, "点击image,OnTouchListener。。。,事件action="+event.getAction());
                    return false;
                }
            });
        }
        
        //上面已经有setOnClickListener,所以该方法不执行
        public void btn_click(View view){
            Log.i(TAG, "点击btn,通过xml绑定的onclick事件处理");
        }

        private void findView() {
            parent=(LinearLayout) findViewById(R.id.parent);
            view=findViewById(R.id.event_test);
            btn=(Button) findViewById(R.id.btn);
            myBtn=(MyBtn) findViewById(R.id.mybtn);
            image=(ImageView) findViewById(R.id.event_image);
        }

    //    @Override
    //    public boolean onTouchEvent(MotionEvent event) {
    //        return mDetector.onTouchEvent(event);
    //    }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            mDetector.onTouchEvent(event);
            return true;
        }
        
        //自定义一个GestureListener,这个是View类下的,别写错哦!!!
        private class MyGestureListener implements OnGestureListener {

            @Override
            public boolean onDown(MotionEvent motionEvent) {
                Log.d(TAG, "onDown:按下");
                CommonUtils.toastShow(getApplicationContext(), "onDown:按下");
                return false;
            }

            @Override
            public void onShowPress(MotionEvent motionEvent) {
                Log.d(TAG, "onShowPress:手指按下一段时间,不过还没到长按");
                CommonUtils.toastShow(getApplicationContext(), "onShowPress:手指按下一段时间,不过还没到长按");
            }

            @Override
            public boolean onSingleTapUp(MotionEvent motionEvent) {
                Log.d(TAG, "onSingleTapUp:手指离开屏幕的一瞬间");
                CommonUtils.toastShow(getApplicationContext(), "onSingleTapUp:手指离开屏幕的一瞬间");
                return false;
            }

            @Override
            public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                Log.d(TAG, "onScroll:在触摸屏上滑动");
                CommonUtils.toastShow(getApplicationContext(), "onScroll:在触摸屏上滑动");
                return false;
            }

            @Override
            public void onLongPress(MotionEvent motionEvent) {
                Log.d(TAG, "onLongPress:长按并且没有松开");
                CommonUtils.toastShow(getApplicationContext(), "onLongPress:长按并且没有松开");
            }

            @Override
            public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                Log.d(TAG, "onFling:迅速滑动,并松开");
                CommonUtils.toastShow(getApplicationContext(), "onFling:迅速滑动,并松开");
                return false;
            }
        }

    }

  • 相关阅读:
    代理的原理
    nodemon:让node自动重启
    http与https的区别
    Content-Type:几种常用数据编码格式
    vue ssr服务端渲染
    vue 实现文件上传和文件下载
    vue element-ui表格里时间戳转换成时间显示
    vue npm start 自动打开网页
    yearProgress.vue
    vuejs中class与style的绑定
  • 原文地址:https://www.cnblogs.com/songfeilong2325/p/5422593.html
Copyright © 2011-2022 走看看