RecyclerView嵌套联动

/**

* RecyclerView -ViewPage-RecyclerView  内层嵌套的RV,  解决上下滑动冲突

*/

public class ParentRecyclerViewextends RecyclerView {

int totalDy =0;

/**

* 用于判断RecyclerView是否在fling

*/

    boolean isStartFling =false;

private int mMaxDistance;

private FlingHelper mFlingHelper;

/**

* 记录上次Event事件的y坐标

*/

    private float lastY;

public int getVelocityY() {

return velocityY;

}

/**

* 记录当前滑动的y轴加速度

*/

    private int velocityY;

private AtomicBoolean canScrollVertically;

private ChildRecyclerView mChildRecyclerView;

private int mMeasuredHeight;

private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;

private VirtualLayoutManager.LayoutParams mLayoutParams;

public ParentRecyclerView(Context context) {

super(context);

init();

}

public ParentRecyclerView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

init();

}

public ParentRecyclerView(Context context, @Nullable AttributeSet attrs,int defStyle) {

super(context, attrs, defStyle);

init();

}

private void init() {

mFlingHelper =new FlingHelper(getContext());

mMaxDistance = mFlingHelper.getVelocityByDistance(UIUtils.getScreenHeight() *4.0);

canScrollVertically =new AtomicBoolean(true);

addOnScrollListener(new OnScrollListener() {

@Override

public void onScrollStateChanged(RecyclerView recyclerView,int newState) {

super.onScrollStateChanged(recyclerView, newState);

//如果父RecyclerView fling过程中已经到底部,需要让子RecyclerView滑动神域的fling

                if (parentRecyclerOnScrollListener !=null){

parentRecyclerOnScrollListener.onScrollStateChanged(recyclerView,newState);

}

if (newState == RecyclerView.SCROLL_STATE_IDLE) {

dispatchChildFling();

}

}

@Override

public void onScrolled(RecyclerView recyclerView,int dx,int dy) {

super.onScrolled(recyclerView, dx, dy);

if (isStartFling) {

totalDy =0;

isStartFling =false;

}

//在RecyclerView fling情况下,记录当前RecyclerView在y轴的偏移

                totalDy += dy;

if (parentRecyclerOnScrollListener !=null){

parentRecyclerOnScrollListener.onScrolled(recyclerView,dx,dy);

}

}

});

onGlobalLayoutListener =new ViewTreeObserver.OnGlobalLayoutListener() {

@Override

public void onGlobalLayout() {

int measuredHeight = getMeasuredHeight();

if (measuredHeight != mMeasuredHeight) {

mMeasuredHeight = measuredHeight;

if (mLayoutParams !=null) {

mLayoutParams.height = (int) (mMeasuredHeight - getResources().getDimension(R.dimen.x40));

}

}

}

};

getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);

}

public interface ParentRecyclerOnScrollListener{

void onScrollStateChanged(RecyclerView recyclerView,int newState);

void onScrolled(RecyclerView recyclerView,int dx,int dy);

}

private ParentRecyclerOnScrollListener parentRecyclerOnScrollListener;

public void setParentRecyclerOnScrollListener(ParentRecyclerOnScrollListener parentRecyclerOnScrollListener) {

this.parentRecyclerOnScrollListener = parentRecyclerOnScrollListener;

}

@Override

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);

}

private void dispatchChildFling() {

if (isScrollEnd() && velocityY !=0) {

double splineFlingDistance = mFlingHelper.getSplineFlingDistance(velocityY);

if (splineFlingDistance > totalDy) {

childFling(mFlingHelper.getVelocityByDistance(splineFlingDistance - totalDy));

}

}

totalDy =0;

velocityY =0;

}

private void childFling(int velY) {

if (mChildRecyclerView !=null) {

mChildRecyclerView.fling(0,velY);

}

}

public void setChildRecyclerView(ChildRecyclerView childRecyclerView) {

this.mChildRecyclerView = childRecyclerView;

}

public VirtualLayoutManager initLayoutManager() {

return new VirtualLayoutManager(getContext()) {

@Override

public int scrollVerticallyBy(int dy, Recycler recycler, State state) {

try {

return super.scrollVerticallyBy(dy, recycler, state);

}catch (Exception e) {

e.printStackTrace();

}

return 0;

}

@Override

public void onLayoutChildren(Recycler recycler, State state) {

try {

super.onLayoutChildren(recycler, state);

}catch (Exception e) {

e.printStackTrace();

}

}

@Override

public boolean canScrollVertically() {

return canScrollVertically.get() || mChildRecyclerView ==null || mChildRecyclerView.isScrollTop();

}

@Override

public void addDisappearingView(View child) {

try {

super.addDisappearingView(child);}catch ( Exception e) {

e.printStackTrace();

}

}

@Override

public RecyclerView.LayoutParams generateDefaultLayoutParams() {

mLayoutParams =new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,

(int) (mMeasuredHeight - getResources().getDimension(R.dimen.x40)));

return mLayoutParams;

}

@Override

public boolean supportsPredictiveItemAnimations() {

return false;

}

};

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

if(ev !=null && ev.getAction() == MotionEvent.ACTION_DOWN) {

//ACTION_DOWN的时候重置加速度

            velocityY =0;

stopScroll();

LogUtil.i("ParentRecyclerView","isScrollTop:"+isScrollTop());

if (!isScrollTop()) {

getParent().requestDisallowInterceptTouchEvent(true);

}

}

if(!(ev ==null || ev.getAction() == MotionEvent.ACTION_MOVE)) {

//在ACTION_MOVE的情况下,将lastY置为0

            lastY =0f;

canScrollVertically.set(!isScrollEnd());

//LogUtil.i("ParentRecyclerView","isScrollEnd:"+isScrollEnd());

        }

return super.dispatchTouchEvent(ev);

}

/****

* 滑动距离及坐标 归还父控件焦点

****/

    private float xLast, yLast;

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

xLast = ev.getX();

yLast = ev.getY();

break;

case MotionEvent.ACTION_MOVE:

final float curX = ev.getX();

final float curY = ev.getY();

final int xDistance = (int) (curX - xLast);

final int yDistance = (int) (curY - yLast);

int orientation = getOrientation(xDistance, yDistance);

if (orientation=='r'||orientation =='l'){

return false;

}

if (orientation =='u' && isScrollEnd()) {

return false;

}

break;

}

return super.onInterceptTouchEvent(ev);

}

private int getOrientation(float dx,float dy) {

if (Math.abs(dx) > Math.abs(dy)) {

//X轴移动

            return dx >0 ?'r' :'l';//右,左

        }else {

//Y轴移动

            return dy >0 ?'d' :'u';//下//上

        }

}

@Override

public boolean fling(int velocityX,int velocityY) {

boolean fling =super.fling(velocityX, velocityY);

if (!fling || velocityY <=0) {

this.velocityY =0;

}else {

isStartFling =true;

this.velocityY = velocityY;

}

return fling;

}

private boolean isScrollEnd() {

//RecyclerView.canScrollVertically(1)的值表示是否能向上滚动,false表示已经滚动到底部

        return !canScrollVertically(1);

}

public void scrollToTop() {

if (!canScrollVertically.get()) {

canScrollVertically.set(true);

}

if (mChildRecyclerView !=null && !mChildRecyclerView.isScrollTop()) {

mChildRecyclerView.scrollToPosition(0);

postDelayed(() -> {

super.scrollToPosition(0);

},20);

}else {

super.scrollToPosition(0);

}

}

public boolean isScrollTop() {

if (mChildRecyclerView !=null && mChildRecyclerView.isScrollTop() && canScrollVertically.get() && !canScrollVertically(-1)) {

return true;

}

return canScrollVertically.get() &&!canScrollVertically(-1);

}

//----------------------------------------------------------------------------------------------

// NestedScroll. fix:当ChildRecyclerView下滑时(手指未放开),ChildRecyclerView滑动到顶部(非fling),此时ParentRecyclerView不会继续下滑。

//----------------------------------------------------------------------------------------------

    @Override

public boolean onStartNestedScroll(View child, View target,int nestedScrollAxes) {

return targetinstanceof ChildRecyclerView;

}

@Override

public void onNestedPreScroll(View target,int dx,int dy,int[] consumed) {

//1.当前Parent RecyclerView没有滑动底,且dy> 0 是下滑

        boolean isParentCanScroll = dy >0 && !isScrollEnd();

//2.当前Child RecyclerView滑到顶部了,且dy < 0,即上滑

        boolean isChildCanNotScroll = !(dy >=0 || mChildRecyclerView ==null || !mChildRecyclerView.isScrollTop());

//以上两种情况都需要让Parent RecyclerView去scroll,和下面onNestedPreFling机制类似

        if(isParentCanScroll || isChildCanNotScroll) {

scrollBy(0,dy);

consumed[1] = dy;

}

}

@Override

public boolean onNestedFling(View target,float velocityX,float velocityY,boolean consumed) {

return true;

}

@Override

public boolean onNestedPreFling(View target,float velocityX,float velocityY) {

boolean isParentCanFling = velocityY >0f && !isScrollEnd();

boolean isChildCanNotFling = !(velocityY >=0 || mChildRecyclerView ==null || !mChildRecyclerView.isScrollTop());

if(!isParentCanFling && !isChildCanNotFling) {

return false;

}

fling(0, (int) velocityY);

return true;

}

//    override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {

//        val childRecyclerView = findNestedScrollingChildRecyclerView()

//        if(isScrollEnd().not() || childRecyclerView == null || childRecyclerView.isScrollTop()) {

//            return super.dispatchNestedPreFling(velocityX, velocityY)

//        }

//        childFling(velocityY.toInt())

//        return true

//    }

//----------------------------------------------------------------------------------------------

// NestedScroll. fix:当ChildRecyclerView下滑时(手指未放开),ChildRecyclerView滑动到顶部(非fling),此时ParentRecyclerView不会继续下滑。

//----------------------------------------------------------------------------------------------

}



/**

* RecyclerView -ViewPage-RecyclerView  内层嵌套的RV,  解决上下滑动冲突

*/

public class ChildRecyclerViewextends RecyclerView {

private FlingHelper mFlingHelper;

private int mVelocityY =0;

private boolean isStartFling =false;

private int  totalDy;

private ParentRecyclerView mParentRecyclerView;

public ChildRecyclerView(Context context) {

super(context);

init();

}

public ChildRecyclerView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

init();

}

public ChildRecyclerView(Context context, @Nullable AttributeSet attrs,int defStyle) {

super(context, attrs, defStyle);

init();

}

private void init() {

mFlingHelper =new FlingHelper(getContext());

setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);

initScrollListener();

}

private void initScrollListener() {

addOnScrollListener(new OnScrollListener() {

@Override

public void onScrolled(RecyclerView recyclerView,int dx,int dy) {

super.onScrolled(recyclerView, dx, dy);

if(isStartFling) {

totalDy =0;

isStartFling =false;

}

totalDy += dy;

}

@Override

public void onScrollStateChanged(RecyclerView recyclerView,int newState) {

if(newState == RecyclerView.SCROLL_STATE_IDLE) {

dispatchParentFling();

}

super.onScrollStateChanged(recyclerView, newState);

}

});

}

private void dispatchParentFling() {

if (mParentRecyclerView ==null) {

mParentRecyclerView =  findParentRecyclerView();

}

if (mParentRecyclerView !=null) {

LogUtil.i("childRecyclerView","isScrollTop:"+isScrollTop()+",mVelocityY:"+mVelocityY);

if(isScrollTop() && mVelocityY !=0) {

//当前ChildRecyclerView已经滑动到顶部,且竖直方向加速度不为0,如果有多余的需要交由父RecyclerView继续fling

                double flingDistance = mFlingHelper.getSplineFlingDistance(mVelocityY);

if(flingDistance > (Math.abs(totalDy))) {

fling(0,-mFlingHelper.getVelocityByDistance(flingDistance + totalDy));

}

//fix 在run方法里面,注意 this@ChildRecyclerView的使用,否则使用的是ParentRecyclerView的变量

                totalDy =0;

mVelocityY =0;

}

}

}

private float downX;//按下时 的X坐标

    private float downY;//按下时 的Y坐标

    @Override

public boolean dispatchTouchEvent(MotionEvent ev) {

float x = ev.getX();

float y = ev.getY();

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

mVelocityY =0;

findParentRecyclerView().setChildRecyclerView(this);

//将按下时的坐标存储

                downX = x;

downY = y;

//                True if the child does not want the parent to

//                intercept touch events.

                getParent().requestDisallowInterceptTouchEvent(true);

break;

case MotionEvent.ACTION_MOVE:

//获取到距离差

                float dx = x - downX;

float dy = y - downY;

//通过距离差判断方向

                int orientation = getOrientation(dx, dy);

switch (orientation) {

//左右滑动交给ViewPager处理

                    case 'r':

case 'l':

getParent().requestDisallowInterceptTouchEvent(false);

break;

case 'u':

if (isScrollEnd())

getParent().requestDisallowInterceptTouchEvent(false);

break;

}

break;

}

return super.dispatchTouchEvent(ev);

}

private int getOrientation(float dx,float dy) {

if (Math.abs(dx) > Math.abs(dy)) {

//X轴移动

            return dx >0 ?'r' :'l';//右,左

        }else {

//Y轴移动

            return dy >0 ?'d' :'u';//下//上

        }

}

@Override

public boolean fling(int velocityX,int velocityY) {

if(!isAttachedToWindow())return false;

boolean fling =super.fling(velocityX, velocityY);

if(!fling || velocityY >=0) {

//fling为false表示加速度达不到fling的要求,将mVelocityY重置

            mVelocityY =0;

}else {

//正在进行fling

            isStartFling =true;

mVelocityY = velocityY;

}

return fling;

}

public boolean isScrollTop() {

//RecyclerView.canScrollVertically(-1)的值表示是否能向下滚动,false表示已经滚动到顶部

        return !canScrollVertically(-1);

}

public boolean isScrollEnd() {

//RecyclerView.canScrollVertically(1)的值表示是否能向上滚动,false表示已经滚动到底部

        return !canScrollVertically(1);

}

private ParentRecyclerView findParentRecyclerView() {

ViewParent parentView = getParent();

while (!(parentViewinstanceof ParentRecyclerView)) {

parentView = parentView.getParent();

}

return (ParentRecyclerView) parentView;

}

}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。