上篇我们学习了 Android 的事件传递机制, 这里就假装大家都懂了啊, 接下来我们继续深入学习事件的传递机制, 首先我们把原来的 demo 中的 ActivityCustomLinearLayoutCustomTextView 类中的 dispatchTouchEventonInterceptTouchEventonTouchEvent 方法的 return 语句复原(上一篇我们为了加深理解改了代码查看效果), 即改为:
- return super.dispatchTouchEvent(event);
- return super.onInterceptTouchEvent(event);
- return super.onTouchEvent(event);
事件的分发流程很复杂, 接下来我们就引入 setOnTouchListener 和 setOnClickListener 来深入讲解, 首先为 ViewGroup 和 ViewActivity 添加 setOnTouchListener 方法并打印日志, 完整代码如下:
- public class TouchEventActivity extends AppCompatActivity {
- private CustomLinearLayout mCustomLinearLayout;
- private CustomTextView mCustomTextView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_touch_event);
- mCustomLinearLayout = (CustomLinearLayout) findViewById(R.id.ll_touch_layout);
- mCustomTextView = (CustomTextView) findViewById(R.id.tv_touch_content);
- mCustomLinearLayout.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 ViewGroup 的 --->onTouch");
- return false;
- }
- });
- mCustomTextView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 View 的 --->onTouch");
- return false;
- }
- });
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 Activity 的 --->dispatchTouchEvent");
- return super.dispatchTouchEvent(event);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 Activity 的 --->onTouchEvent");
- return super.onTouchEvent(event);
- }
- }
运行, 点击, 日志如下:
这里看到的跟我们上篇博文的运行效果差不多, 但还是有差别的, ViewGroup 和 View 我们所设置的 setOnTouchListener 被执行了, 即在执行事件消费 (onTouchEvent) 方法前会先执行用户设置的 onTouch 方法细心的同学也发现了, 我们在 onTouch 方法里返回默认值 false, 如果我们把它改为返回 true 呢, 改一下代码:
- mCustomLinearLayout.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 ViewGroup 的 --->onTouch");
- return true;
- }
- });
- mCustomTextView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 View 的 --->onTouch");
- return true;
- }
- });
运行, 点击, 日志如下:
我们从日志可以看出 ACTION_DOWNACIONT_MOVEACTION_UP 三个事件都会传递到 View 里, 但是都会在 onTouch 方法里终止传递, 且并没有执行 onTouchEvent 事件消费方法, 所以结论是: 用户设置的 setOnTouchListener 会在 onTouchEvent 方法前调用, 并且 onTouch 的返回值是 false 的话, 会继续执行 onTouchEvent 方法, 如果返回 true 的话, 表明该事件被消费了, 事件终止传递(我们在 ViewGroup 也设置了 setOnTouchListener 方法, 但没有被执行, 也是这个原因), 并不会执行 onTouchEvent 方法
我们再来验证一下, 现在我们把 View(CustomTextView)的 onTouch 返回值改回 false, 然后 ViewGroup(CustomLinearLayout)的 onTouch 返回值保持 true 不变, 代码如下:
- mCustomLinearLayout.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 ViewGroup 的 --->onTouch");
- return true;
- }
- });
- mCustomTextView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 View 的 --->onTouch");
- return false;
- }
- });
运行, 打印日志, 如下:
ACTION_DOWN 分发到 View 的 onTouch, 此时返回值为 false, 没有消费事件, 继续执行 onTouchEvent 方法, 该返回一样返回 false, 事件再往上传递给 ViewGroup 的 onTouchEvent 方法, 因为我们为 ViewGroup 设置了 setOnTouchListener, 所以在执行 onTouchEvent 之前会先执行 onTouch, 而我们在 onTouch 方法返回了 true, 表明该事件被消费, 接着传递终止, 所以 ViewGroup 的 onTouchEvent 方法并没有被执行
由于 ACTION_DOWN 的事件被 ViewGroup 消费掉了, 所以接下来的 ACTION_MOVEACTION_UP 事件就直接传递给 ViewGroup,View 就无法接收到事件了
来源: https://juejin.im/entry/5a9558906fb9a0634912ca81