Android滑动冲突处理
在我们平时项目开发的过程中,是不是会遇到滑动View之间的相互嵌套,比如外部的Scrollview或SwipeRefreshLayout嵌套内部的Viewpager或横向Recyclerview,如外部Viewpager嵌套内部Viewpager。这样往往就会造成滑动的冲突导致不流畅甚至根本滑不动。事件分发机制见我的另一篇Android事件分发机制,用事实说话。
滑动冲突产生的两个根本原因:
- 外部滑动方向与内部方向不一致
- 外部滑动方向与内部方向一致
扎心了,一不一致都可能造成滑动冲突。第一种如ScrollView 嵌套ViewPager,第二种如ViewPager嵌套ViewPager。
解决方案:
外部拦截法:
从父View着手,重写onInterceptTouchEvent方法,在父View需要拦截的时候拦截事件,不需要则不拦截返回false。伪代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(父View拦截条件){
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
下面我们来看一个真实的案例:
SwipeRefreshLayout嵌套ViewPager,需要流畅横向滑动Viewpager,这种操作下通常会顺带牵连到SwipeRefreshLayout致其拉动,如示例:
用外部拦截法自定义一个MySwipeRefreshLayout,在开始滑动的条件下,判断手势的横纵方向的距离大小,判断用户到底是要横向滑动还是纵向滑动。纵向滑动意为拉动刷新,就拦截事件,横向滑动就意为滑动viewpager,就不拦截事件。解决效果:
giphy.gif
MySwipeRefreshLayout代码如下:
import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
public class MySwipeRefreshLayout extends SwipeRefreshLayout{
private float startX;
private float startY;
private float mTouchSlop;
public MySwipeRefreshLayout(Context context) {
super(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startX = ev.getX();
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float distanceX = Math.abs(ev.getX() - startX);
float distanceY = Math.abs(ev.getY() - startY);
if(distanceX > mTouchSlop && distanceX > distanceY){ //判断为横向滑动
return false;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
}
getScaledTouchSlop()表示能触发滚动的最小距离,如果小于这个距离就不触发移动控件。
/**
* @return Distance in pixels a touch can wander before we think the user is scrolling
*/
public int getScaledTouchSlop() {
return mTouchSlop;
}
内部拦截法:(getParent().requestDisallowInterceptTouchEvent(true),请求父容器不拦截事件)
从子View入手,重写子元素的dispatchTouchEvent方法,父View先不要拦截任何事件,所有的事件传递给子View,如果子View需要此事件就消费掉,不需要此事件的话就通过requestDisallowInterceptTouchEvent(true)方法交给父View处理。伪代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if(事件交给父View条件){
getParent().requestDisallowInterceptTouchEvent(false);
}else{
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
}
return super.onInterceptTouchEvent(ev);
}
父View需要重写:默认不拦截子View事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN){
return false;
}else{
return true;
}
}
案例:Viewpager嵌套多个ViewPager:需求是默认滑动内部Viewpager,当内部Viewpager滑动到首页或者末页时,就滑动外部Viewpager切换页面。解决效果:
giphy.gif
自定义子Viewpager:
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class InerViewPager extends ViewPager{
private int itemCount;
public InerViewPager(Context context) {
super(context);
}
public InerViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
itemCount = getAdapter().getCount();
if(getCurrentItem() == 0 || getCurrentItem() == itemCount - 1){
getParent().requestDisallowInterceptTouchEvent(false);
}else{
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
}
return super.onInterceptTouchEvent(ev);
}
}
推荐阅读
-
Android滑动组件悬浮固定在顶部效果
-
Android 高仿微信朋友圈动态支持双击手势放大并滑动查看图片效果
-
Android事件分发机制(下) View的事件处理
-
Android滑动优化高仿QQ6.0侧滑菜单(滑动优化)
-
Android动画处理缩放,平移,旋转 博客分类: Android多媒体技术 旋转平移缩放安卓动画
-
Android接收和发送短信处理
-
PHP的session_start()和require_once起冲突了怎么处理
-
Android中listview嵌套scrollveiw冲突的解决方法
-
从源代码分析Android Universal ImageLoader的缓存处理机制
-
Android编程实现canvas绘制柱状统计图功能【自动计算宽高及分度值、可左右滑动】