Android点击事件分发笔记
Start:
Activity:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("June", "dispatchTouchEvent: Activity dispatchTouchEvent"); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e("June", "Activity onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e("June", "Activity onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_CANCEL: Log.e("June", "Activity onTouchEvent ACTION_CANCEL"); break; case MotionEvent.ACTION_UP: Log.e("June", "Activity onTouchEvent ACTION_UP"); break; default: break; } Log.e("June", "onTouchEvent: Activity onTouchEvent"); return super.onTouchEvent(event); } }
CustomLinearLayout:
public class CustomLinearLayout extends LinearLayout { public CustomLinearLayout(Context context) { super(context); } public CustomLinearLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CustomLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.e("June", "onInterceptTouchEvent: ViewGroup onInterceptTouchEvent"); return super.onInterceptTouchEvent(ev); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("June", "dispatchTouchEvent: ViewGroup dispatchTouchEvent"); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e("June", "ViewGroup onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e("June", "ViewGroup onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_CANCEL: Log.e("June", "ViewGroup onTouchEvent ACTION_CANCEL"); break; case MotionEvent.ACTION_UP: Log.e("June", "ViewGroup onTouchEvent ACTION_UP"); break; default: break; } Log.e("June", "onTouchEvent: ViewGroup onTouchEvent"); return super.onTouchEvent(event); } }
CustomTextView:
public class CustomTextView extends android.support.v7.widget.AppCompatTextView { public CustomTextView(Context context) { super(context); } public CustomTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CustomTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.e("June", "dispatchTouchEvent: View dispatchTouchEvent"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e("June", "View onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e("June", "View onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_CANCEL: Log.e("June", "View onTouchEvent ACTION_CANCEL"); break; case MotionEvent.ACTION_UP: Log.e("June", "View onTouchEvent ACTION_UP"); break; default: break; } Log.e("June", "onTouchEvent: View onTouchEvent"); return super.onTouchEvent(event); } }
Preview:
1、全部return super,点击页面查看日志:
可以看出我们没有对控件里面的方法进行重写或更改返回值,而直接用super调用父类的默认实现,那么整个事件流向应该是从Activity---->ViewGroup--->View 从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View--->ViewGroup--->Activity从下往上调用onTouchEvent方法。
2、将Activity中的dispatchTouchEvent() reture true
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("June", "dispatchTouchEvent: Activity dispatchTouchEvent"); return true; }
根据日志可以看出点击事件并没有往下层的View去传递,也没有调用onTouchEvent()方法;
将Activity中的dispatchTouchEvent() return super ,CustomLinearLayout中的dispatchTouchEvent() return true
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("June", "dispatchTouchEvent: ViewGroup dispatchTouchEvent"); return true; }
最顶层Activity依然正常分发事件到ViewGroup中停止往下分发给子View,也没有调用任何onTouchEvent()方法;
重复上述步骤将CustomTextView中的dispatchTouchEvent()方法return true
同样;
将CustomTextView中的onTouchEvent()方法return true
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e("June", "View onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e("June", "View onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_CANCEL: Log.e("June", "View onTouchEvent ACTION_CANCEL"); break; case MotionEvent.ACTION_UP: Log.e("June", "View onTouchEvent ACTION_UP"); break; default: break; } Log.e("June", "onTouchEvent: View onTouchEvent"); return true; }
分发事件依然正常进行,消费事件在最底层的View中停止向上传递;(同样的return true就不多示意)
结论:dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止传递了(到达终点)(没有谁能再收到这个事件)。看下图中只要return true事件就没再继续传下去了,对于return true我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了
3、将CustomLinearLayout中的dispatchTouchEvent()方法return false
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("June", "dispatchTouchEvent: ViewGroup dispatchTouchEvent"); return false; }
下面是将CustomTextView中的dispatchTouchEvent()方法return false的Log日志
可以看出将dispatchTouchEvent()方法return false,事件都会回传给父View的onTouchEvent()方法去进行处理
下面我们将CustomLinearLayoutonTouchEvent() return false看一下什么情况:
可以看出来好像跟上面的全部return super日志输出是一样的?理一下逻辑:因为只有CustomLinearLayoutonTouchEvent() return false 其他的都是return super ,所以Activity传递给ViewGroup传递给子View,子View调用onTouchEvent()向上传递给ViewGroup调用onTouchEvent()传递给Activity调用onTouchEvent(),那没错了,就是一样的。(CustomTextView的onTouchEvent()return false就不过多展示)
结论:对于dispatchTouchEvent() 返回 false ,事件停止往子View传递和分发同时开始往父控件回溯(父控件的onTouchEvent()开始从下往上回传直到某个onTouchEvent() return true),事件分发机制就像递归,return false 的意义就是递归停止然后开始回溯。对于onTouchEvent() return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。
接下来看一下ViewGroup中的onInterceptTouchEvent()方法
Inercept:拦截,意思就是ViewGroup每次在做分发的时候,问一问拦截器要不要拦截(问问自己这个事件要不要自己来处理)如果要自己处理那就在onInterceptTouchEvent()方法中 return true就会交给自己的onTouchEvent()的处理,如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent()拦截器return super.onInterceptTouchEvent()和return false是一样的,是不会拦截的,事件会继续往子View的dispatchTouchEvent()传递。
总结一下吧:
- 对于 dispatchTouchEvent(),onTouchEvent(),return true是终结事件传递。return false 是回溯到父View的onTouchEvent()方法。
- ViewGroup 想把自己分发给自己的onTouchEvent,需要拦截器onInterceptTouchEvent()方法return true 把事件拦截下来。
- ViewGroup 的拦截器onInterceptTouchEvent() 默认是不拦截的,所以return super.onInterceptTouchEvent() = return false;
- View 没有拦截器,为了让View可以把事件分发给自己的onTouchEvent(),View的dispatchTouchEvent()默认实现(super)就是把事件分发给自己的onTouchEvent()
End;