android - scrollView和listview滑动冲突
巴扎黑
巴扎黑 2017-04-17 17:56:37
0
2
496

在scrollview内嵌套了一个viewpager,重写了scrollview的onInterceptTouchEvent()方法,但是我写的没有什么效果,最近刚刚接触这个滑动冲突不能很好理解。

public class ScrollViewX extends ScrollView {

    private static final String TAG = "ScrollViewX";

    private ViewPager mViewPager;

    private int mLastXIntercepted = 0;
    private int mLastYIntercepted = 0;

    public ScrollViewX(Context context) {
        super(context);
    }

    public ScrollViewX(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollViewX(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        boolean intercepted = false;

        int x = (int) ev.getX();
        int y = (int) ev.getY();
        int deltaX = x - mLastXIntercepted;
        int deltaY = y - mLastYIntercepted;

        mLastXIntercepted = x;
        mLastYIntercepted = y;

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                //action_down不拦截
                intercepted = false;
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if(mViewPager != null && isTouchInView(mViewPager, ev)){
                    //点击事件发生在viewpager范围内
                    if(Math.abs(deltaY) > Math.abs(deltaX)) {
                        //如果竖直方向的滑动距离大于横向, 那么scrollview拦截
                        intercepted = true;
                    } else {
                        intercepted = false;
                    }
                } else {
                    intercepted = false;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default: break;
        }
        return intercepted;
    }

    //判断点击事件是否在当前view中
    private boolean isTouchInView(View view, MotionEvent event) {
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();
        int[] local = new int[2];
        view.getLocationOnScreen(local);
        int subVX = local[0];
        int subVY = local[1];
        int subWidth = view.getWidth();
        int subHeight = view.getHeight();
        if(x > subVX && x < subVX + subWidth && y > subVY && y < subVY + subHeight) {
            return true;
        }
        return false;
    }

    public void setViewPager(ViewPager viewPager) {
        mViewPager = viewPager;
    }
}

我在红色部分左右滑动viewpager能够正常,但是在viewpager中竖直滑动就不能滚动scrollview,但是我觉得我在scrollview的onInterceptTouchEvent()方法中已经判断了,但是最终却没有效果。

感谢采纳的那位,根据他的提示, 我顺便解决了listview的滑动冲突。
现在使用外部拦截法: 重写ScrollView 的 onInterceptedTouchEvent() 方法,

public class ScrollViewX extends ScrollView {

    private static final String TAG = "ScrollViewX";

    private ListViewX mListViewX;
    private ViewPager mViewPager;

    private int mLastX = 0;
    private int mLastY = 0;

    public ScrollViewX(Context context) {
        super(context);
    }

    public ScrollViewX(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollViewX(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        boolean intercepted = false;

        int x = (int) ev.getX();
        int y = (int) ev.getY();

        int deltaX = x - mLastX;
        int deltaY = y - mLastY;

        Log.i(TAG, "deltaY = " + deltaY);

        mLastX = x;
        mLastY = y;

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                return super.onInterceptTouchEvent(ev);
            }
            case MotionEvent.ACTION_MOVE: {
                if(mViewPager != null && isTouchInView(mViewPager, ev)){
                    //点击事件发生在viewpager范围内
                    if(Math.abs(deltaY) > Math.abs(deltaX)) {
                        //如果竖直方向的滑动距离大于横向, 那么scrollview拦截
                        return true;
                    } else {
                        return super.onInterceptTouchEvent(ev);
                    }
                } else if(mListViewX != null && isTouchInView(mListViewX, ev)) {
                    if(atTopOrEnd(deltaY)) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    return super.onInterceptTouchEvent(ev);
                }
            }
            case MotionEvent.ACTION_UP: {
                return super.onInterceptTouchEvent(ev);
            }
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    //如果listView滑到顶端时当前事件向上滑动,需要scrollview接管, 在底端时类似。
    private boolean atTopOrEnd(int len) {
        int count = mListViewX.getCount();
        int topId = mListViewX.getFirstVisiblePosition();
        int endId = mListViewX.getLastVisiblePosition();
        if((endId == count - 1 && len < 0)) {
            View lastView = mListViewX.getChildAt(mListViewX.getChildCount() - 1);
            if(lastView.getBottom() == mListViewX.getHeight()) {
                return true;
            }
        }
        if(topId == 0 && len > 0) {
            View firstView = mListViewX.getChildAt(topId);
            if(firstView.getTop() == 0) {
                return true;
            }
        }
        return false;
    }

    //判断点击事件是否在当前view中
    private boolean isTouchInView(View view, MotionEvent event) {
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();
        int[] local = new int[2];
        view.getLocationOnScreen(local);
        int subVX = local[0];
        int subVY = local[1];
        int subWidth = view.getWidth();
        int subHeight = view.getHeight();
        if(x > subVX && x < subVX + subWidth && y > subVY && y < subVY + subHeight) {
            return true;
        }
        return false;
    }

    public void setListViewX(ListViewX listViewX) {
        mListViewX = listViewX;
    }

    public void setViewPager(ViewPager viewPager) {
        mViewPager = viewPager;
    }
}

采用内部拦截法: 重写listview 的 dispatchTouchEvent() 方法

public class ListViewX extends ListView {

    private static final String TAG = "ListViewX";

    private int mLastX = 0;
    private int mLastY = 0;

    public ListViewX(Context context) {
        super(context);
    }

    public ListViewX(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ListViewX(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //ListView 在 ScrollView中显示需要处理
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            width = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
            height = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
        } else if(widthMode == MeasureSpec.AT_MOST) {
            width = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
            height = heightMeasureSpec;
        } else if(heightMode == MeasureSpec.AT_MOST) {
            width = widthMeasureSpec;
            height = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
        } else {
            width = widthMeasureSpec;
            height = heightMeasureSpec;
        }
        super.onMeasure(width, height);
    }

    //requestDisallowInterceptTouchEvent参数为false表示父容器拦截
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int x = (int) ev.getX();
        int y = (int) ev.getY();

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                //父容器不拦截
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if(atTopOrEnd(deltaY)) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }

        mLastX = x;
        mLastY = y;

        return super.dispatchTouchEvent(ev);
    }

    //如果listView滑到顶端时当前事件向上滑动,需要scrollview接管, 在底端时类似。
    private boolean atTopOrEnd(int len) {
        int count = getCount();
        int topId = getFirstVisiblePosition();
        int endId = getLastVisiblePosition();
        if((endId == count - 1 && len < 0)) {
            View lastView = getChildAt(getChildCount() - 1);
            if(lastView.getBottom() == getHeight()) {
                return true;
            }
        }
        if(topId == 0 && len > 0) {
            View firstView = getChildAt(topId);
            if(firstView.getTop() == 0) {
                return true;
            }
        }
        return false;
    }
}
巴扎黑
巴扎黑

全員に返信(2)
洪涛

ViewPager の onTouch イベントがジェスチャを「食べた」ため、ジェスチャが垂直方向の動きの場合は、false を返します。

いいねを押す +0
伊谢尔伦

リストビューのサブ項目の幅と高さを測定する方法を書き直すには、オンラインで多数検索できます

いいねを押す +0
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート