View的事件分发机制

1.通过日志来分析事件传递相关方法的执行流程

为了更好的了解view的事件传递,我们可以自定义一个button继承自Button,并重写事件传递相关方法通过打日志来分析各个方法的执行顺序。
自定义button,重写dispatchTouchEvent和onTouchEvent   view plain  copy
  1. public class MyButton extends Button {  
  2.     public MyButton(Context context, AttributeSet attrs) {  
  3.         super(context, attrs);  
  4.     }  
  5.   
  6.     @Override  
  7.     public boolean dispatchTouchEvent(MotionEvent event) {  
  8.         switch (event.getAction()) {  
  9.             case MotionEvent.ACTION_DOWN:  
  10.                 Log.d("dispatchTouchEvent""ACTION_DOWN");  
  11.                 break;  
  12.             case MotionEvent.ACTION_MOVE:  
  13.                 Log.d("dispatchTouchEvent""ACTION_MOVE");  
  14.                 break;  
  15.             case MotionEvent.ACTION_UP:  
  16.                 Log.d("dispatchTouchEvent""ACTION_UP");  
  17.                 break;  
  18.         }  
  19.         return super.dispatchTouchEvent(event);  
  20.     }  
  21.   
  22.     @Override  
  23.     public boolean onTouchEvent(MotionEvent event) {  
  24.         switch (event.getAction()) {  
  25.             case MotionEvent.ACTION_DOWN:  
  26.                 Log.d("onTouchEvent""ACTION_DOWN");  
  27.                 break;  
  28.             case MotionEvent.ACTION_MOVE:  
  29.                 Log.d("onTouchEvent""ACTION_MOVE");  
  30.                 break;  
  31.             case MotionEvent.ACTION_UP:  
  32.                 Log.d("onTouchEvent""ACTION_UP");  
  33.                 break;  
  34.         }  
  35.         return super.onTouchEvent(event);  
  36.     }  
  37. }  
将自定义button添加到布局中
[html]  view plain  copy
  1. <LinearLayout  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:id="@+id/activity_main"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     tools:context="com.tfx.test.MainActivity">  
  8.   
  9.     <com.tfx.test.MyButton  
  10.         android:id="@+id/bt"  
  11.         android:layout_width="wrap_content"  
  12.         android:layout_height="wrap_content"  
  13.         android:text="button"/>  
  14. LinearLayout>  
MainActivity代码,给自定义button注册setOnTouchEvent监听
[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity {  
  2.     @Override  
  3.     protected void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_main);  
  6.         Button button = (Button) findViewById(R.id.bt);  
  7.         button.setOnTouchListener(new View.OnTouchListener() {  
  8.             @Override  
  9.             public boolean onTouch(View view, MotionEvent motionEvent) {  
  10.                 switch (motionEvent.getAction()) {  
  11.                     case MotionEvent.ACTION_DOWN:  
  12.                         Log.d("onTouch""ACTION_DOWN");  
  13.                         break;  
  14.   
  15.                     case MotionEvent.ACTION_MOVE:  
  16.                         Log.d("onTouch""ACTION_MOVE");  
  17.                         break;  
  18.   
  19.                     case MotionEvent.ACTION_UP:  
  20.                         Log.d("onTouch""ACTION_UP");  
  21.                         break;  
  22.                 }  
  23.                 return false;  
  24.             }  
  25.         });  
  26.     }  
  27. }  
重写了三个事件分发相关方法后,运行demo并单击button按钮,然后看输出的日志
[java]  view plain  copy
  1. 12-19 09:22:56.478 1837-1837/com.tfx.test D/dispatchTouchEvent: ACTION_DOWN  
  2. 12-19 09:22:56.478 1837-1837/com.tfx.test D/onTouch: ACTION_DOWN  
  3. 12-19 09:22:56.478 1837-1837/com.tfx.test D/onTouchEvent: ACTION_DOWN  
  4. 12-19 09:22:56.556 1837-1837/com.tfx.test D/dispatchTouchEvent: ACTION_UP  
  5. 12-19 09:22:56.556 1837-1837/com.tfx.test D/onTouch: ACTION_UP  
  6. 12-19 09:22:56.556 1837-1837/com.tfx.test D/onTouchEvent: ACTION_UP  
如果点击按钮时手指稍微蹭一下,会执行多次move,如下
[java]  view plain  copy
  1. 12-19 09:20:17.253 1837-1837/com.tfx.test D/dispatchTouchEvent: ACTION_DOWN  
  2. 12-19 09:20:17.253 1837-1837/com.tfx.test D/onTouch: ACTION_DOWN  
  3. 12-19 09:20:17.253 1837-1837/com.tfx.test D/onTouchEvent: ACTION_DOWN  
  4. 12-19 09:20:17.295 1837-1837/com.tfx.test D/dispatchTouchEvent: ACTION_MOVE  
  5. 12-19 09:20:17.296 1837-1837/com.tfx.test D/onTouch: ACTION_MOVE  
  6. 12-19 09:20:17.296 1837-1837/com.tfx.test D/onTouchEvent: ACTION_MOVE  
  7. 12-19 09:20:17.463 1837-1837/com.tfx.test D/dispatchTouchEvent: ACTION_MOVE  
  8. 12-19 09:20:17.463 1837-1837/com.tfx.test D/onTouch: ACTION_MOVE  
  9. 12-19 09:20:17.463 1837-1837/com.tfx.test D/onTouchEvent: ACTION_MOVE  
  10. 12-19 09:20:17.580 1837-1837/com.tfx.test D/dispatchTouchEvent: ACTION_MOVE  
  11. 12-19 09:20:17.580 1837-1837/com.tfx.test D/onTouch: ACTION_MOVE  
  12. 12-19 09:20:17.580 1837-1837/com.tfx.test D/onTouchEvent: ACTION_MOVE  
  13. 12-19 09:20:17.716 1837-1837/com.tfx.test D/dispatchTouchEvent: ACTION_UP  
  14. 12-19 09:20:17.717 1837-1837/com.tfx.test D/onTouch: ACTION_UP  
  15. 12-19 09:20:17.717 1837-1837/com.tfx.test D/onTouchEvent: ACTION_UP  
从日志我们大致可以看出,不管是down、move、up执行的流程都是dispatchTouchEvent --> onTouch --> onTouchEvent。
onTouchListener的onTouch方法是有返回值的,如果把返回值改为return true再运行,会发现onTouchEvent方法得不到执行,这是为什么呢?此时若再给按钮注册单击事件,会发现单击事件也不会被触发,这又是为什么呢? 带着这两个问题,接下来我们对dispatchTouchEvent方法进行分析。

2.dispatchTouchEvent方法

进入button的父类TextView我们并没有发现该方法,继续在它的父类view里面找,此时就能看到dispatchTouchEvent方法的源码,如下:
[java]  view plain  copy
  1. public boolean dispatchTouchEvent(MotionEvent event) {    
  2.     if (!onFilterTouchEventForSecurity(event)) {    
  3.         return false;    
  4.     }    
  5.   
  6.     if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&    
  7.             mOnTouchListener.onTouch(this, event)) {    
  8.         return true;    
  9.     }    
  10.     return onTouchEvent(event);    
  11. }  
主要就看第二个if语句,这里会有三个条件:
条件一:对mOnTouchListener进行非空判断,也就是判断是否注册了onTouchListener监听
条件二:判断view是否是enable,一般控件默认都是enable
条件三:判断onTouchEvent的onTouch方法是否返回true
如果这三个条件都成立,那么dispatchTouchEvent方法就返回true,否则执行onTouchEvent并返回。
接着说上面的第一个问题,当三个条件都满足了,dispatchTouchEvent方法就会返回ture,onTouchEvent方法将得不到执行,也就是说onTouchListener的onTouch方法的返回值决定了onTouchEvent方法能否得到执行,如果返回true事件将会被消费,不再继续传递给onTouchEvent方法,默认返回false,onTouchEvent方法将得到执行,到这里第一个问题就解决了。
接着说问题二,根据问题一,我们可以断定单击事件的onClick方法是在onTouchEvent方法中执行的,只有onTouchEvent方法得到执行,才能响应单击事件。还有onTouch方法的执行优先于onClick方法,onTouch方法做的比onClick更多,能响应dowm、move、up多个手势。

3.onTouchEvent方法


你可能感兴趣的:(Android)