RecyclerView嵌套RecyclerView滑动问题解决

背景

目前市面上大多数App首页加推荐效果基本实现方式为:RecyclerView + ViewPager + RecyclerView。其中面临的最主要问题就是RecyclerView同方向滑动冲突解决。

效果

在这里插入图片描述
在这里插入图片描述

前言

滑动冲突解决方案目前最流行的方式NestedScrollingParent和NestedScrollingChild,大家有不了解的可以手动google一下相关知识。
而且最主要的是RecyclerView已经内部实现了NestedScrollingChild。故本文通过最外层RecyclerView实现NestedScrollingParent2接口解决嵌套滑动问题。

备注:对于NestedScrollingParent2和NestedScrollingParent的区别大家自定google一下

实现

1.首先要做的是使得touch事件可以传递到子RcyclerView中去。

通过RecyclerView现有的接口即可实现,主要通过requestDisallowInterceptTouchEvent实现touch事件的穿透。

package com.android.app.sample;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;


/**
 * Intercept touch event of RecyclerView binded
 */
public class SwipeItemTouchListener implements RecyclerView.OnItemTouchListener {

    public SwipeItemTouchListener() {
    }

    @Override
    public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) {
        //判断当前事件坐标点下是否包含RecyclerView
        recyclerView.requestDisallowInterceptTouchEvent(findScrollableChildViewUnder(recyclerView, motionEvent) != null);
        return false;
    }

    @Override
    public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) {

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean b) {

    }

    private View findScrollableChildViewUnder(RecyclerView recyclerView, MotionEvent event) {
        if (recyclerView == null) {
            return null;
        }
        final int x = (int) event.getX();
        final int y = (int) event.getY();
        View child = recyclerView.findChildViewUnder(x, y);

        if (findCanScrollView(child) != null) {
            return child;
        }

        return null;
    }

    private View findCanScrollView(View v) {
        if (v instanceof ViewGroup) {
            ViewGroup target = (ViewGroup) v;
            if (target instanceof RecyclerView
                    && target.getVisibility() == View.VISIBLE) {
                return target;
            } else {
                for (int i = 0; i < target.getChildCount(); ++i) {
                    View view = findCanScrollView(target.getChildAt(i));
                    if (view != null) {
                        return view;
                    }
                }
                return null;
            }
        } else {
            return null;
        }
    }

}

2.自定义RecyclerView实现NestedScrollingParent2接口处理child的嵌套滚动

大多数接口通过NestedScrollingParentHelper进行实现相对简单。核心逻辑在onNestedPreScroll中处理消费时机及距离。

package com.android.app.sample.widget;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.NestedScrollingParent2;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class NestedRecyclerView extends RecyclerView implements NestedScrollingParent2 {

    private static final String TAG = NestedRecyclerView.class.getSimpleName();

    private NestedScrollingParentHelper mParentHelper;

    public NestedRecyclerView(@NonNull Context context) {
        super(context);
        init();
    }

    public NestedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public NestedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mParentHelper = new NestedScrollingParentHelper(this);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull View view, @NonNull View target, int axes, int type) {
        Log.d(TAG, "onStartNestedScroll axes = " + axes);
        return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }

    @Override
    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
        Log.d(TAG, "onNestedScrollAccepted: ");
        mParentHelper.onNestedScrollAccepted(child, target, axes, type);
        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, type);
    }

    @Override
    public void onStopNestedScroll(@NonNull View target, int type) {
        Log.d(TAG, "onStopNestedScroll: ");
        mParentHelper.onStopNestedScroll(target, type);
        stopNestedScroll(type);
    }

    @Override
    public void onStopNestedScroll(@NonNull View target) {
        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);
    }

    @Override
    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        Log.d(TAG, "onNestedScroll dxConsumed = " + dxConsumed + ",, dyConsumed = " + dyConsumed + ",,dxUnconsumed = " + dxUnconsumed + ",,dyUnconsumed" + dyUnconsumed);
    }

    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @Nullable int[] consumed, int type) {

        // dy > 0 向上活动 dy < 0 向下滑动
        boolean consum = false;
        if (dy > 0){
            consum = canScrollVertically(dy) || !target.canScrollVertically(dy);
        } else {
            consum = !target.canScrollVertically(dy);
        }
        if (consum){
            scrollBy(0, dy);
            if (consumed != null) {
                consumed[1] = dy;
            }
        }
        Log.d(TAG, "onNestedPreScroll:::dy::" + dy + ",,consumed[1]::" + (consumed != null ? consumed[1] : ""));

    }

    @Override
    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
                               int dxUnconsumed, int dyUnconsumed) {
        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, ViewCompat.TYPE_TOUCH);
    }

    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
    }

    @Override
    public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY,
                                 boolean consumed) {
        return false;
    }

    @Override
    public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
        return false;
    }

    @Override
    public int getNestedScrollAxes() {
        return mParentHelper.getNestedScrollAxes();
    }

}

源码

https://github.com/panliangxiao/RVLayoutManager

发布了26 篇原创文章 · 获赞 0 · 访问量 1112

猜你喜欢

转载自blog.csdn.net/Plx0303sunny/article/details/105678211
今日推荐