View的滑动冲突

滑动冲突一般是在界面中,内外两层同时可以滑动,这个时候就会产生滑动冲突。
1、常见的滑动冲突场景
场景1:外部滑动方向和内部滑动方向不一致
场景2:外部滑动方向和内部滑动方向一致
场景3:场景1和2嵌套
分别如下:
在这里插入图片描述
场景1主要是将ViewPager和Fragment配合使用,达到页面滑动的效果,可以通过左右滑动,切换页面,而在每个页面又会又ListView。如果使用的是ViewPager,就不需要注意这个问题,因为ViewPager内部已经处理了这个冲突。如果是Scroll View等,就需要我们手动处理这个滑动冲突。
场景2,出现在页面中,两层滑动在同一个方向,因为在滑动化成中,系统无法判断是滑动那一层,要么就是两层同时滑动,显得很卡顿,要么只有一层滑动。一般就上下同向,或者左右同向。
场景3,是场景1和场景2的嵌套,相当于一个LlideMenu效果,内部有一个ViewPager,ViewPager的内部又有一个ListView。这种其实是单个简单的冲突叠加,只需要处理好内部和中间层的冲突,中间层和外层的冲突即可。
2、滑动冲突的处理规则
场景1:当用户左右滑动时,需要让外部的View拦截,当上下滑动时,让内部的View拦截。这样根据特征,即根据水平和竖直方向滑动的距离差、两个方向的夹角、两个方向的速度差,来判断是由谁来拦截事件。
场景2:场景2比较特殊,因为它无法根据滑动的角度、距离差以及速度差判断。但是这种情况一般是业务功能上有特殊需求,比如达到什么状态,让外部View响应,另一个状态,让内部View响应等。这样我们就可以参照场景一做相应的拦截。
场景3:处理方式和场景1、2类似,也需要参照具体的业务需求。
3.滑动冲突的拦截方式
3.1外部拦截
外部拦截,其实就是所有点击事件都先经过父容器拦截处理,如果父容器需要此事就拦截,如果不用就不拦截。这样符合滑动冲突的问题,也比较适合事件的分发机制。外部拦截的方法是需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可:

public boolean onInterceptTouchEvent(MotonEvent 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;	
		}
		default:
		break;
	}
	mLastXInrercept = x;
	mLastYIntercepr = y;
	return intercepted;
}

上述代码是外部拦截得典型逻辑,针对不同得滑动冲突,只需要修改父容器需要当前点击事件得条件即可。注意得一点,当ACTION_DOWN这个事件发生,父容器必须返回false,即不拦截,原因是因为如果父容器拦截,那么后续得事件都会交由父容器处理。ACTION_UP,同理,也需返回false,因为,如果父容器得UP事件返回true,而事件又需要交给子View去响应,那么子元素得onClick事件就无法触发。
3.2、内部拦截
内部拦截是指父容器不做任何拦截,所有事件都交由子元素处理,如果子元素需要处理,直接消耗,如果不处理,再交由父容器,这种方式和Android中的事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent才能正常工作使用起来稍复杂,需要重写子元素的dispatchTouchEvent:

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:{
			int deltaX = x - mLastX;
			int deltaY = y - mLastY;
			if(父容器需要此类点击事件){
				parent.requestDisallowInterceptTouchEvent(false);	
			}
			break;
		}
		default:
		break;
	}
	mLastX = x;
	mLastY = y;
	return super.dispatchTouchEvent(event);
}

上面代码是典型的内部拦截方法,除了子元素要处理以外,父元素也要默认拦截除了ACTION_DOWN以外的其它事件,这样当子元素调用parent.requestDisal-lowIntercepteTouchEvent(false)方法时,父元素才能继续拦截所需要的事件。

特别声明:内容总结来源《Android开发艺术探索》,仅记录学习,如有侵权或不对之处,还请告知,定当删除或改正

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这个Bug有点难搞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值