android view事件分发机制

简介: 首先我们先写个简单的例子来测试View的事件转发的流程~ 1、案例 为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志~ MyButton [java] view plaincopy package com.example.zhy_event03;      import a
+关注继续查看

首先我们先写个简单的例子来测试View的事件转发的流程~

1、案例

为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志~

MyButton

  1. package com.example.zhy_event03;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.widget.Button;  
  8.   
  9. public class MyButton extends Button  
  10. {  
  11.     private static final String TAG = MyButton.class.getSimpleName();  
  12.   
  13.     public MyButton(Context context, AttributeSet attrs)  
  14.     {  
  15.         super(context, attrs);  
  16.     }  
  17.   
  18.     @Override  
  19.     public boolean onTouchEvent(MotionEvent event)  
  20.     {  
  21.         int action = event.getAction();  
  22.   
  23.         switch (action)  
  24.         {  
  25.         case MotionEvent.ACTION_DOWN:  
  26.             Log.e(TAG, "onTouchEvent ACTION_DOWN");  
  27.             break;  
  28.         case MotionEvent.ACTION_MOVE:  
  29.             Log.e(TAG, "onTouchEvent ACTION_MOVE");  
  30.             break;  
  31.         case MotionEvent.ACTION_UP:  
  32.             Log.e(TAG, "onTouchEvent ACTION_UP");  
  33.             break;  
  34.         default:  
  35.             break;  
  36.         }  
  37.         return super.onTouchEvent(event);  
  38.     }  
  39.       
  40.     @Override  
  41.     public boolean dispatchTouchEvent(MotionEvent event)  
  42.     {  
  43.         int action = event.getAction();  
  44.   
  45.         switch (action)  
  46.         {  
  47.         case MotionEvent.ACTION_DOWN:  
  48.             Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");  
  49.             break;  
  50.         case MotionEvent.ACTION_MOVE:  
  51.             Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");  
  52.             break;  
  53.         case MotionEvent.ACTION_UP:  
  54.             Log.e(TAG, "dispatchTouchEvent ACTION_UP");  
  55.             break;  
  56.   
  57.         default:  
  58.             break;  
  59.         }  
  60.         return super.dispatchTouchEvent(event);  
  61.     }  
  62.   
  63.       
  64. }  

在onTouchEvent和dispatchTouchEvent中打印了日志~

然后把我们自定义的按钮加到主布局文件中;

布局文件:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context=".MainActivity" >  
  6.   
  7.     <com.example.zhy_event03.MyButton  
  8.         android:id="@+id/id_btn"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:text="click me" />  
  12.   
  13. </LinearLayout>  

最后看一眼MainActivity的代码
  1. package com.example.zhy_event03;  
  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.OnTouchListener;  
  9. import android.widget.Button;  
  10.   
  11. public class MainActivity extends Activity  
  12. {  
  13.     protected static final String TAG = "MyButton";  
  14.     private Button mButton ;  
  15.     @Override  
  16.     protected void onCreate(Bundle savedInstanceState)  
  17.     {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.activity_main);  
  20.           
  21.         mButton = (Button) findViewById(R.id.id_btn);  
  22.         mButton.setOnTouchListener(new OnTouchListener()  
  23.         {  
  24.             @Override  
  25.             public boolean onTouch(View v, MotionEvent event)  
  26.             {  
  27.                 int action = event.getAction();  
  28.   
  29.                 switch (action)  
  30.                 {  
  31.                 case MotionEvent.ACTION_DOWN:  
  32.                     Log.e(TAG, "onTouch ACTION_DOWN");  
  33.                     break;  
  34.                 case MotionEvent.ACTION_MOVE:  
  35.                     Log.e(TAG, "onTouch ACTION_MOVE");  
  36.                     break;  
  37.                 case MotionEvent.ACTION_UP:  
  38.                     Log.e(TAG, "onTouch ACTION_UP");  
  39.                     break;  
  40.                 default:  
  41.                     break;  
  42.                 }  
  43.                   
  44.                 return false;  
  45.             }  
  46.         });  
  47.     }  
  48.   
  49.       
  50. }  

在MainActivity中,我们还给MyButton设置了OnTouchListener这个监听~

好了,跟View事件相关一般就这三个地方了,一个onTouchEvent,一个dispatchTouchEvent,一个setOnTouchListener;

下面我们运行,然后点击按钮,查看日志输出:

  1. 08-31 06:09:39.030: E/MyButton(879): dispatchTouchEvent ACTION_DOWN  
  2. 08-31 06:09:39.030: E/MyButton(879): onTouch ACTION_DOWN  
  3. 08-31 06:09:39.049: E/MyButton(879): onTouchEvent ACTION_DOWN  
  4. 08-31 06:09:39.138: E/MyButton(879): dispatchTouchEvent ACTION_MOVE  
  5. 08-31 06:09:39.138: E/MyButton(879): onTouch ACTION_MOVE  
  6. 08-31 06:09:39.147: E/MyButton(879): onTouchEvent ACTION_MOVE  
  7. 08-31 06:09:39.232: E/MyButton(879): dispatchTouchEvent ACTION_UP  
  8. 08-31 06:09:39.248: E/MyButton(879): onTouch ACTION_UP  
  9. 08-31 06:09:39.248: E/MyButton(879): onTouchEvent ACTION_UP  

我有意点击的时候蹭了一下,不然不会触发MOVE,手抖可能会打印一堆MOVE的日志~~~

好了,可以看到,不管是DOWN,MOVE,UP都会按照下面的顺序执行:

1、dispatchTouchEvent

2、 setOnTouchListener的onTouch

3、onTouchEvent

下面就跟随日志的脚步开始源码的探索~

2、dispatchTouchEvent

首先进入View的dispatchTouchEvent

  1. /** 
  2.      * Pass the touch screen motion event down to the target view, or this 
  3.      * view if it is the target. 
  4.      * 
  5.      * @param event The motion event to be dispatched. 
  6.      * @return True if the event was handled by the view, false otherwise. 
  7.      */  
  8.     public boolean dispatchTouchEvent(MotionEvent event) {  
  9.         if (!onFilterTouchEventForSecurity(event)) {  
  10.             return false;  
  11.         }  
  12.   
  13.         if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
  14.                 mOnTouchListener.onTouch(this, event)) {  
  15.             return true;  
  16.         }  
  17.         return onTouchEvent(event);  
  18.     }  

直接看13行:首先判断mOnTouchListener不为null,并且view是enable的状态,然后 mOnTouchListener.onTouch(this, event)返回true,这三个条件如果都满足,直接return true ; 也就是下面的onTouchEvent(event)不会被执行了;

那么mOnTouchListener是和方神圣,我们来看看:

  1. /** 
  2.   * Register a callback to be invoked when a touch event is sent to this view. 
  3.   * @param l the touch listener to attach to this view 
  4.   */  
  5.  public void setOnTouchListener(OnTouchListener l) {  
  6.      mOnTouchListener = l;  
  7.  }  
其实就是我们在Activity中设置的setOnTouchListener。

也就是说:如果我们设置了setOnTouchListener,并且return true,那么View自己的onTouchEvent就不会被执行了,当然了,本例我们return false,我们还得往下探索 ;

已经解决一个常见的问题:View的onTouchListener和onTouchEvent的调用关系,相信大家应该已经明白了~let's go;继续往下。

 3、View的onTouchEvent:

接下来是View的onTouchEvent:

  1. /** 
  2.      * Implement this method to handle touch screen motion events. 
  3.      * 
  4.      * @param event The motion event. 
  5.      * @return True if the event was handled, false otherwise. 
  6.      */  
  7.     public boolean onTouchEvent(MotionEvent event) {  
  8.         final int viewFlags = mViewFlags;  
  9.   
  10.         if ((viewFlags & ENABLED_MASK) == DISABLED) {  
  11.             // A disabled view that is clickable still consumes the touch  
  12.             // events, it just doesn't respond to them.  
  13.             return (((viewFlags & CLICKABLE) == CLICKABLE ||  
  14.                     (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));  
  15.         }  
  16.   
  17.         if (mTouchDelegate != null) {  
  18.             if (mTouchDelegate.onTouchEvent(event)) {  
  19.                 return true;  
  20.             }  
  21.         }  
  22.   
  23.         if (((viewFlags & CLICKABLE) == CLICKABLE ||  
  24.                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
  25.             switch (event.getAction()) {  
  26.                 case MotionEvent.ACTION_UP:  
  27.                     boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  
  28.                     if ((mPrivateFlags & PRESSED) != 0 || prepressed) {  
  29.                         // take focus if we don't have it already and we should in  
  30.                         // touch mode.  
  31.                         boolean focusTaken = false;  
  32.                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {  
  33.                             focusTaken = requestFocus();  
  34.                         }  
  35.   
  36.                         if (!mHasPerformedLongPress) {  
  37.                             // This is a tap, so remove the longpress check  
  38.                             removeLongPressCallback();  
  39.   
  40.                             // Only perform take click actions if we were in the pressed state  
  41.                             if (!focusTaken) {  
  42.                                 // Use a Runnable and post this rather than calling  
  43.                                 // performClick directly. This lets other visual state  
  44.                                 // of the view update before click actions start.  
  45.                                 if (mPerformClick == null) {  
  46.                                     mPerformClick = new PerformClick();  
  47.                                 }  
  48.                                 if (!post(mPerformClick)) {  
  49.                                     performClick();  
  50.                                 }  
  51.                             }  
  52.                         }  
  53.   
  54.                         if (mUnsetPressedState == null) {  
  55.                             mUnsetPressedState = new UnsetPressedState();  
  56.                         }  
  57.   
  58.                         if (prepressed) {  
  59.                             mPrivateFlags |= PRESSED;  
  60.                             refreshDrawableState();  
  61.                             postDelayed(mUnsetPressedState,  
  62.                                     ViewConfiguration.getPressedStateDuration());  
  63.                         } else if (!post(mUnsetPressedState)) {  
  64.                             // If the post failed, unpress right now  
  65.                             mUnsetPressedState.run();  
  66.                         }  
  67.                         removeTapCallback();  
  68.                     }  
  69.                     break;  
  70.   
  71.                 case MotionEvent.ACTION_DOWN:  
  72.                     if (mPendingCheckForTap == null) {  
  73.                         mPendingCheckForTap = new CheckForTap();  
  74.                     }  
  75.                     mPrivateFlags |= PREPRESSED;  
  76.                     mHasPerformedLongPress = false;  
  77.                     postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());  
  78.                     break;  
  79.   
  80.                 case MotionEvent.ACTION_CANCEL:  
  81.                     mPrivateFlags &= ~PRESSED;  
  82.                     refreshDrawableState();  
  83.                     removeTapCallback();  
  84.                     break;  
  85.   
  86.                 case MotionEvent.ACTION_MOVE:  
  87.                     final int x = (int) event.getX();  
  88.                     final int y = (int) event.getY();  
  89.   
  90.                     // Be lenient about moving outside of buttons  
  91.                     int slop = mTouchSlop;  
  92.                     if ((x < 0 - slop) || (x >= getWidth() + slop) ||  
  93.                             (y < 0 - slop) || (y >= getHeight() + slop)) {  
  94.                         // Outside button  
  95.                         removeTapCallback();  
  96.                         if ((mPrivateFlags & PRESSED) != 0) {  
  97.                             // Remove any future long press/tap checks  
  98.                             removeLongPressCallback();  
  99.   
  100.                             // Need to switch from pressed to not pressed  
  101.                             mPrivateFlags &= ~PRESSED;  
  102.                             refreshDrawableState();  
  103.                         }  
  104.                     }  
  105.                     break;  
  106.             }  
  107.             return true;  
  108.         }  
  109.   
  110.         return false;  
  111.     }  

代码还是比较长的,

10-15行,如果当前View是Disabled状态且是可点击则会消费掉事件(return true);可以忽略,不是我们的重点;

17-21行,如果设置了mTouchDelegate,则会将事件交给代理者处理,直接return true,如果大家希望自己的View增加它的touch范围,可以尝试使用TouchDelegate,这里也不是重点,可以忽略;

接下来到我们的重点了:

23行的判断:如果我们的View可以点击或者可以长按,则,注意IF的范围,最终一定return true ;

 if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
           //...
            return true;
        }

接下来就是   switch (event.getAction())了,判断事件类型,DOWN,MOVE,UP等;

我们按照例子执行的顺序,先看  case MotionEvent.ACTION_DOWN (71-78行):

1、MotionEvent.ACTION_DOWN

75行:给mPrivateFlags设置一个PREPRESSED的标识

76行:设置mHasPerformedLongPress=false;表示长按事件还未触发;

77行:发送一个延迟为ViewConfiguration.getTapTimeout()的延迟消息,到达延时时间后会执行CheckForTap()里面的run方法:

1、ViewConfiguration.getTapTimeout()为115毫秒;

2、CheckForTap

  1. private final class CheckForTap implements Runnable {  
  2.       public void run() {  
  3.           mPrivateFlags &= ~PREPRESSED;  
  4.           mPrivateFlags |= PRESSED;  
  5.           refreshDrawableState();  
  6.           if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {  
  7.               postCheckForLongClick(ViewConfiguration.getTapTimeout());  
  8.           }  
  9.       }  
  10.   }  

在run方法里面取消mPrivateFlags的PREPRESSED,然后设置PRESSED标识,刷新背景,如果View支持长按事件,则再发一个延时消息,检测长按;

  1. private void postCheckForLongClick(int delayOffset) {  
  2.        mHasPerformedLongPress = false;  
  3.   
  4.        if (mPendingCheckForLongPress == null) {  
  5.            mPendingCheckForLongPress = new CheckForLongPress();  
  6.        }  
  7.        mPendingCheckForLongPress.rememberWindowAttachCount();  
  8.        postDelayed(mPendingCheckForLongPress,  
  9.                ViewConfiguration.getLongPressTimeout() - delayOffset);  
  10.    }  

  1. class CheckForLongPress implements Runnable {  
  2.   
  3.         private int mOriginalWindowAttachCount;  
  4.   
  5.         public void run() {  
  6.             if (isPressed() && (mParent != null)  
  7.                     && mOriginalWindowAttachCount == mWindowAttachCount) {  
  8.                 if (performLongClick()) {  
  9.                     mHasPerformedLongPress = true;  
  10.                 }  
  11.             }  
  12.         }  

可以看到,当用户按下,首先会设置标识为PREPRESSED,如果在115毫秒内抬起了,UP时会移除CheckForTap这个回调(UP时会分析);

如果115后,没有抬起,会将View的标识设置为PRESSED且去掉PREPRESSED标识,然后发出一个检测长按的延迟任务,延时为:ViewConfiguration.getLongPressTimeout() - delayOffset(500ms -115ms),这个115ms刚好时检测额PREPRESSED时间;也就是用户从DOWN触发开始算起,如果500ms内没有抬起则认为触发了长按事件:

1、如果此时设置了长按的回调,则执行长按时的回调,且如果长按的回调返回true;才把mHasPerformedLongPress置为ture;

2、否则,如果没有设置长按回调或者长按回调返回的是false;则mHasPerformedLongPress依然是false;

好了DOWN就分析完成了;大家回个神,下面回到VIEW的onTouchEvent中的ACTION_MOVE:

2、MotionEvent.ACTION_MOVE

86到105行:

87-88行:拿到当前触摸的x,y坐标;

91行判断当然触摸点有没有移出我们的View,如果移出了:

1、执行removeTapCallback(); 

2、然后判断是否包含PRESSED标识,如果包含,移除长按的检查:removeLongPressCallback();

3、最后把mPrivateFlags中PRESSED标识去除,刷新背景;

  1. private void removeTapCallback() {  
  2.        if (mPendingCheckForTap != null) {  
  3.            mPrivateFlags &= ~PREPRESSED;  
  4.            removeCallbacks(mPendingCheckForTap);  
  5.        }  
  6.    }  
这个是移除,DOWN触发时设置的PREPRESSED的检测;即当前触发时机在DOWN触发不到115ms时,你就已经移出控件外了;

如果115ms后,你才移出控件外,则你的当前mPrivateFlags一定为PRESSED且发送了长按的检测;

就会走上面的2和3;首先移除removeLongPressCallback()
 private void removeLongPressCallback() {
        if (mPendingCheckForLongPress != null) {
          removeCallbacks(mPendingCheckForLongPress);
        }
    }

然后mPrivateFlags中PRESSED标识去除,刷新背景;

好了,MOVE我们也分析完成了,总结一下:只要用户移出了我们的控件:则将mPrivateFlags取出PRESSED标识,且移除所有在DOWN中设置的检测,长按等;

下面再回个神,回到View的onTouchEvent的ACTION_UP:

3、MotionEvent.ACTION_UP

26到69行:

27行:判断mPrivateFlags是否包含PREPRESSED

28行:如果包含PRESSED或者PREPRESSED则进入执行体,也就是无论是115ms内或者之后抬起都会进入执行体。

36行:如果mHasPerformedLongPress没有被执行,进入IF

38行:removeLongPressCallback();移除长按的检测

45-50行:如果mPerformClick如果mPerformClick为null,初始化一个实例,然后立即通过handler添加到消息队列尾部,如果添加失败则直接执行 performClick();添加成功,在mPerformClick的run方法中就是执行performClick();

终于执行了我们的click事件了,下面看一下performClick()方法:

  1. public boolean performClick() {  
  2.        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
  3.   
  4.        if (mOnClickListener != null) {  
  5.            playSoundEffect(SoundEffectConstants.CLICK);  
  6.            mOnClickListener.onClick(this);  
  7.            return true;  
  8.        }  
  9.   
  10.        return false;  
  11.    }  

if (mOnClickListener != null) {    
            mOnClickListener.onClick(this);
            return true;
        }

久违了~我们的mOnClickListener ;

别激动,还没结束,回到ACTION_UP,

58行:如果prepressed为true,进入IF体:

为mPrivateFlags设置表示为PRESSED,刷新背景,125毫秒后执行mUnsetPressedState

否则:mUnsetPressedState.run();立即执行;也就是不管咋样,最后mUnsetPressedState.run()都会执行;

看看这个UnsetPressedState主要干什么:

  private final class UnsetPressedState implements Runnable {
        public void run() {
            setPressed(false);
        }
    }

 public void setPressed(boolean pressed) {
        if (pressed) {
            mPrivateFlags |= PRESSED;
        } else {
            mPrivateFlags &= ~PRESSED;
        }
        refreshDrawableState();
        dispatchSetPressed(pressed);
    }

把我们的mPrivateFlags中的PRESSED取消,然后刷新背景,把setPress转发下去。

ACTION_UP的最后一行:removeTapCallback(),如果mPendingCheckForTap不为null,移除;

4、总结

好了,代码跨度还是相当大的,下面需要总结下:

1、整个View的事件转发流程是:

View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。

2、onTouchEvent中的DOWN,MOVE,UP

DOWN时:

a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap,如果115ms内抬起手指,触发了UP,则不会触发click事件,并且最终执行的是UnsetPressedState对象,setPressed(false)将setPress的传递下去;这种情况很少发生,可能只会在压力测试的时候会发现无法触发click事件;

b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息;

c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:

此时如果LongClickListener不为null,则会执行回调,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;

MOVE时:

主要就是检测用户是否划出控件,如果划出了:

115ms内,直接移除mPendingCheckForTap;

115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();

UP时:

a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;

b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;

c、如果是500ms以后,那么有两种情况:

i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;

ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,点击事件OnClick事件依然可以触发;

d、最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;


最后问个问题,然后再运行个例子结束:

1、setOnLongClickListener和setOnClickListener是否只能执行一个

不是的,只要setOnLongClickListener中的onClick返回false,则两个都会执行;返回true则会屏幕setOnClickListener

最后我们给MyButton同时设置setOnClickListener和setOnLongClickListener,运行看看:

  1. package com.example.zhy_event03;  
  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.OnLongClickListener;  
  10. import android.view.View.OnTouchListener;  
  11. import android.widget.Button;  
  12. import android.widget.Toast;  
  13.   
  14. public class MainActivity extends Activity  
  15. {  
  16.     protected static final String TAG = "MyButton";  
  17.     private Button mButton ;  
  18.     @Override  
  19.     protected void onCreate(Bundle savedInstanceState)  
  20.     {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.activity_main);  
  23.           
  24.         mButton = (Button) findViewById(R.id.id_btn);  
  25.         mButton.setOnTouchListener(new OnTouchListener()  
  26.         {  
  27.             @Override  
  28.             public boolean onTouch(View v, MotionEvent event)  
  29.             {  
  30.                 int action = event.getAction();  
  31.   
  32.                 switch (action)  
  33.                 {  
  34.                 case MotionEvent.ACTION_DOWN:  
  35.                     Log.e(TAG, "onTouch ACTION_DOWN");  
  36.                     break;  
  37.                 case MotionEvent.ACTION_MOVE:  
  38.                     Log.e(TAG, "onTouch ACTION_MOVE");  
  39.                     break;  
  40.                 case MotionEvent.ACTION_UP:  
  41.                     Log.e(TAG, "onTouch ACTION_UP");  
  42.                     break;  
  43.                 default:  
  44.                     break;  
  45.                 }  
  46.                   
  47.                 return false;  
  48.             }  
  49.         });  
  50.         mButton.setOnClickListener(new OnClickListener()  
  51.         {  
  52.             @Override  
  53.             public void onClick(View v)  
  54.             {  
  55.                 Toast.makeText(getApplicationContext(), "onclick",Toast.LENGTH_SHORT).show();  
  56.             }  
  57.         });  
  58.           
  59.         mButton.setOnLongClickListener(new OnLongClickListener()  
  60.         {  
  61.             @Override  
  62.             public boolean onLongClick(View v)  
  63.             {  
  64.                 Toast.makeText(getApplicationContext(), "setOnLongClickListener",Toast.LENGTH_SHORT).show();  
  65.                 return false;  
  66.             }  
  67.         });  
  68.     }  
  69.   
  70.       
  71. }  
效果图:


可以看到LongClickListener已经ClickListener都触发了~


最后,本篇博文完成了对View的事件分发机制的整个流程的说明,并且对源码进行了分析;

当然了,View结束,肯定到我们的ViewGroup了,请点击:Android ViewGroup事件分发机制

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
Android开发
Android 获取include标签中的控件属性并设置事件
Android 获取include标签中的控件属性并设置事件
30 0
|
5月前
|
设计模式 缓存 前端开发
Android 架构之 MVI 究极体 | 状态和事件分道扬镳,粘性不再是问题
Android 架构之 MVI 究极体 | 状态和事件分道扬镳,粘性不再是问题
239 0
|
8月前
|
Java Linux API
“framework必会”系列:Android Input系统(一)事件读取机制
曾经在开发的很长一段时间内,笔者对点击事件的认知只存在于自定义View中的`onTouchEvent`等方法的处理。 后来慢慢的接触到`Android的事件分发机制`,但也只是在**Activity->ViewGroup->View**层面的分发逻辑
|
8月前
|
Android开发
android 模拟发送多点触摸事件
android 模拟发送多点触摸事件
168 0
|
API Android开发
关于Android 日历事件的实现
经常购买火车票,机票的同学就知道,当我们买下一张票的时候,票的行程日期会被写入系统日历中,当火车开动或者飞机启航的前30分钟,手机会有提醒信息,这条信息是由系统日历发出的,提醒用户,别错过时间啦。 像这种系统日历提醒功能,实现起来并不难,毕竟Android 系统已经提供API给我们调用了,不需要重新造轮子,下面我们来实现这个功能。
200 0
|
开发工具 Android开发
RxBus 一个简易、非反射的Android事件通知库
RxBus 一个简易、非反射的Android事件通知库
414 0
RxBus 一个简易、非反射的Android事件通知库
|
设计模式 安全 Java
Android事件通知工具:RxBus在Eclipse和AS中的实践
Android事件通知工具:RxBus在Eclipse和AS中的实践
172 0
Android事件通知工具:RxBus在Eclipse和AS中的实践
|
Android开发
Android Touch事件分发(源码分析)
Android一文让你轻松搞定Touch事件分发 源码分析 Activity事件分发机制 Activity.dispatchTouchEvent()源码 Activity.onTouchEvent()源码 Activity源码总结 ViewGroup事件分发机制 ViewGroup.dispatchTouchEvent()源码 ViewGroup.onInterceptTouchEvent()源码 ViewGroup.onTouchEvent()源码 ViweGroup源码总结 View的事件分发机制 View.dispatchTouchEvent()源码
115 0
Android Touch事件分发(源码分析)
|
Android开发
Android一文让你轻松搞定Touch事件分发(下)
实例 创建实例 创建MyViewGroup继承ViewGroup 创建MyView继承View 创建TouchActivity继承Activity 创建布局文件 MLog.logEvent() 点击页面,看效果 点击Activity(白色区域) 点击ViewGroup(黄色区域) 点击View(蓝色区域) 结果分析 事件分发和处理 Activity处理和分发 Activity处理 运行结果 Activity分发 ViewGroup拦截处理和分发 ViewGroup拦截处理 运行结果 结果分析 运行结果 ViewGroup分发 View处理和分发 View处理 运行结果 结果分析 View分发
83 0
Android一文让你轻松搞定Touch事件分发(下)
|
调度 Android开发
Android一文让你轻松搞定Touch事件分发(上)
前言 名词了解 什么是事件 事件流 什么是事件分发 思路梳理 ViewGroup View 涉及事件分发的方法 方法的简单用途解析 拥有上述方法的类 事件分发流程
272 0
Android一文让你轻松搞定Touch事件分发(上)
相关产品
云迁移中心
推荐文章
更多