Android滑动冲突处理

作者: 奔跑吧李博 | 来源:发表于2017-12-14 18:49 被阅读169次

在我们平时项目开发的过程中,是不是会遇到滑动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致其拉动,如示例:

giphy.gif

用外部拦截法自定义一个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);
    }

}

这里举的是基本栗子,那如果产品经理的想象力丰富,难度系数5.0以上呢?要是开发中遇到较复杂的滑动冲突,我只能说来一个杀一个,来两个杀一双!

相关文章

  • Android滑动冲突处理

    导言 Android中的滑动冲突很常见,例如ScrollView/ListView,ViewPager/ViewP...

  • Android滑动冲突处理

    在我们平时项目开发的过程中,是不是会遇到滑动View之间的相互嵌套,比如外部的Scrollview或SwipeRe...

  • android 事件分发原理

    事件分发在 android 中实在是太重要了,滑动冲突的前置基础知识,滑动冲突不会处理,那么大部分页面效果是写不出...

  • android事件分发机制

    android中的事件处理,以及解决滑动冲突问题都离不开事件分发机制,android中的事件流,即MotionEv...

  • ScrollView 嵌套 RecyclerVeiw, 轻松解决

    滑动冲突 在开发android中, 滑动冲突是一常见的事件冲突。列如:在scrollView中嵌套listView...

  • Android滑动事件冲突解决方法

    1.Android滑动事件冲突解决办法 滑动事件的冲突包括两种情形: 不同方向的滑动冲突:比如ScrollView...

  • Android 嵌套滑动冲突的处理

    移动开发中滑动冲突很常见,也有多种处理方式,今天谈下我现在的处理策略(基本和系统处理策略一致) 最常见的滑动冲突:...

  • 滑动冲突处理

    分2种:内部拦截和外部拦截 外部拦截 viewgroup用onInterceptTouchEvent处理,对需要的...

  • Android System——事件传递(一) View

    Android开发中,在很多的滑动控件嵌套的情况下经常会出现滑动事件冲突等等。以及在自定义控件的时候,需要处理触摸...

  • 滑动冲突处理(一)

    滑动冲突场景:外层横向滑动,内层竖向滑动。这里通过外层拦截的方式来处理冲突。 注意:例子中得布局是ViewGrou...

网友评论

  • 奔跑吧李博:嗯,默认父view不拦截事件,当子view不需要使用时间时,就请求父view拦截事件
  • 碎碎想:getParent().requestDisallowInterceptTouchEvent(true)指是否禁用掉事件拦截的功能吧?默认是false指的是不禁用,所以事件拦截中主要靠onInterceptTouchEvent这个方法来决定

本文标题:Android滑动冲突处理

本文链接:https://www.haomeiwen.com/subject/jrmvixtx.html