View 的事件分发

  1. 事件分发机制
    1.1. 事件分发的顺序:
    Activity -> ViewGroup -> View
    1.2. 事件分发涉及到的方法
    public boolean dispatchTouchEvent(MotionEvent ev) 事件过来的时候首先拜访这个方法,由这个方法决定,是不是需要向子类下发,如果有父类可以向父类分发(需配合requestDisallowInterceptTouchEvent使用)。
    public boolean onInterceptTouchEvent(MotionEvent ev)分发过来的事件是不是要终止,如果终止不向子View 传递,否则反之。
    public boolean onTouchEvent(MotionEvent ev) 用来处理事件。
    三者的区分伪代码如下:

    public boolean dispatchTouchEvent(MotionEvent ev){
    	boolean consume = false;
    	if(onInterceptTouchEvent(ev)){
    		consume  = onTouchEvent(ev);
    	}else{
    		consume  = child.dispatchTouchEvent(ev)
    	}
    	return consume;
    }
    

    所以点击事件优先级 onTouch —> onTouchEvent—>onClick

  2. 事件拦截机制
    有了上面的基础,下面一张图就可以明白了:
    在这里插入图片描述

    如果父类在 onInterceptTouchEvent 方法中 down 事件 return true,那么子View 根本收不到任何的事件。因为在这里已经拦截消费掉了。
    如果在parent View 不拦截事件情况下 ,子View 如果设置了 getParent().requestDisallowInterceptTouchEvent(true),
    那么首次父 事件 的 down不会拦截,在子View接收到down事件后,设置了父 的 事件拦截(getParent().requestDisallowInterceptTouchEvent(true)),子View onTouchEvent 返回 true 消费事件,那么以后的事件消费都在子View 进行了。
    具体这块有个链接不错 Android事件传递之onInterceptTouchEvent()和requestDisallowInterceptTouchEvent()方法的使用

    类比吃苹果,event 是一个苹果,爷爷拿着苹果如果不吃,会给爸爸,如果爸爸不吃会给儿子,如果儿子吃掉,就消费掉了苹果,如果儿子不吃,就返回给爸爸,爸爸不吃就返回给爷爷,如果爷爷不吃,那么ActivityonTouchEvent 方法就结束了。

  3. 滑动冲突解决方式
    3.1. 外部拦截法
    外部拦截法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可,如下面伪代码所示:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
    	boolean intercepted = false;
    	int x=(int) event.getX();
    	int y=(int) event.getY();
    	switch (event.getAction()) {
    	case MotionEvent.ACTION_DOWN:
    		intercepted=false;
    		break;
    
    	case MotionEvent.ACTION_MOVE:
    		if(父容器需要点击事件){
    			intercepted=true;
    		}else{
    			intercepted=false;
    		}
    		break;
    		
    	case MotionEvent.ACTION_UP:
    		intercepted=false;
    		break;
    	default:
    		break;
    	}
    
    	return intercepted;
    }
    

    针对不同的滑动冲突只需要修改父容器需要点击事件这个条件即可,其他均不需要且不能修改。
    在父容器的onInterceptTouchEvent方法中,首先是ACTION_DOWN事件,父容器必须返回false,即不拦截ACTION_DOWN事件,这是因为一旦父容器拦截者这个事件,那么后续的ACTION_MOVEACTION_UP事件都会直接交由父容器处理,这个时候就没法传递给子元素了;其次是ACTION_MOVE事件,这个事件可以根据需要来决定是否拦截;最后是ACTION_UP,这里必须返回false,如果父容器返回true,就会导致子元素无法接收到ACTION_UP事件,这个时候onClick事件就无法触发。

    3.2 内部拦截法
    内部拦截法是指父容器不拦截任何事件,所有事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器处理。我们需要重写子元素的dispatchTouchEvent方法,一下是伪代码:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
    	int x = (int) event.getX();
    	int y = (int) event.getY();
    	switch (event.getAction()) {
    	case MotionEvent.ACTION_DOWN:
    		parent.requestDisallowInterceptTouchEvent(true);// 表示不拦截
    		break;
    
    	case MotionEvent.ACTION_MOVE:
    		if (父容器需要点击事件) {
    			parent.requestDisallowInterceptTouchEvent(false);// 表示拦截
    		}
    		break;
    
    	case MotionEvent.ACTION_UP:
    		break;
    	default:
    		break;
    	}
    	return super.dispatchTouchEvent(event);
    }
    

    针对不同的滑动冲突只需要修改父容器需要点击事件这个条件即可,其他均不需要且不能修改。
    除了子元素需要处理以外,父元素也需要处理除ACTION_DOWN以外的其他事件,这样子当子元素调用 parent.requestDisallowInterceptTouchEvent(false) 时,父元素才能继续拦截所需事件。
    参考 常见的滑动冲突场景及解决方案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值