Behavior搭建多RecyclerView的复杂界面

Behavior搭建多RecycleView的复杂界面之MultiRecycleContainer

  • 开发意图
  • 开发思路
  • demo视图
  • 代码部分
    • 1.自定义RecyclerView容器
    • 2.Behavior部分
  • 使用部分
    • 1.xml布局
    • 2.java部分
  • 遗留
  • 我的项目

开发意图

Android原生开发多个RecyclerView的复杂界,如淘宝首页这样的界面。不可避免的遇到滑动冲突的问题,官方为我们提供了NestedScrollView可以很完美的解决滑动问题。这里记录一下在没有了解NestedScrollView源码的情况下,自己采用behavior的解决思路。

Behavior搭建多RecyclerView的复杂界面_第1张图片

开发思路

1.自定义RecyclerView容器(只包含一个RecyclerView可以添加其他布局)
2.把自定义容器部署到CoordinatorLayout内
2.使用Behavior统一管理自定义容器(位置及滑动事件)

demo视图

Behavior搭建多RecyclerView的复杂界面_第2张图片

代码部分

1.自定义RecyclerView容器

package cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.recyclerview.widget.RecyclerView;

import cn.demomaster.huan.quickdeveloplibrary.util.QDLogger;

@CoordinatorLayout.DefaultBehavior(MultiRecycleBehavior.class)
public class MultiRecycleContainer extends FrameLayout {
    private RecyclerView recyclerView;

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

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

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

    @Override
    protected boolean addViewInLayout(View child, int index, ViewGroup.LayoutParams params) {
        return super.addViewInLayout(child, index, params);
    }

    private void init(AttributeSet attrs) {
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getViewTreeObserver().removeOnGlobalLayoutListener(this);
                findRecyclerView(MultiRecycleContainer.this);
            }
        });
    }

    /**
     * 查找唯一的一个RecyclerView
     * @param child
     */
    private void findRecyclerView(View child) {
        if (child instanceof ViewGroup && !(child instanceof RecyclerView)) {
            for (int i = 0; i < ((ViewGroup) child).getChildCount(); i++) {
                View v = ((ViewGroup) child).getChildAt(i);
                findRecyclerView(v);
            }
        } else if (child instanceof RecyclerView) {
            recyclerView = (RecyclerView) child;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        QDLogger.d("onTouchEvent=" + event);
        /**
         * 把自身及其他子空间的触摸事件传递给recyclerView,其他控件如果不使用触摸滑动可以在ontouch中消耗掉即可
         */
        if (recyclerView != null) {
            recyclerView.dispatchTouchEvent(event);
        }
        return true;
        //return super.onTouchEvent(event);
    }

    private int mHeaderViewHeight;
    private int mRecycleViewHeight;
    private int mViewHeight;

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        findRecyclerView(this);
        if (recyclerView == null) return;
        QDLogger.d("onSizeChanged w=" + w + ",h=" + h + ",oldw=" + oldw + ",oldh=" + oldh);
        if (w != oldw || h != oldh) {
            // mHeaderViewHeight = findViewById(R.id.header).getMeasuredHeight();
            mRecycleViewHeight = recyclerView.getMeasuredHeight();
            mViewHeight = mHeaderViewHeight + mRecycleViewHeight;
        }
        QDLogger.d("mHeaderViewHeight=" + mHeaderViewHeight);
    }

    public int getHeaderViewHeight() {
        return mHeaderViewHeight;
    }

    public int getViewHeight() {
        return mViewHeight;
    }
}

MultiRecycleContainer部分和RecyclerView为一对一的关系,主要负责管理一个recyclerView并且可以添加其他自定义的布局

2.Behavior部分

package cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout;

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.OverScroller;

import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.MotionEventCompat;
import androidx.core.view.VelocityTrackerCompat;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;

import cn.demomaster.huan.quickdeveloplibrary.util.QDLogger;

public class MultiRecycleBehavior extends CoordinatorLayout.Behavior<MultiRecycleContainer> {

    CoordinatorLayout parent;
    @Override
    public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull MultiRecycleContainer child, int layoutDirection) {
        QDLogger.d("onLayoutChild");
        parent.onLayoutChild(child, layoutDirection);
        //获取上一个MultiRecycleContainer容器
        MultiRecycleContainer previous = getPreviousChild(parent, child);
        if (previous == null) {//为空说明child的position=0
            int offset = child.getTop();
            QDLogger.d("SlidingBehavior", child.getId() + "offsetTopAndBottom=" + offset + ", top=" + child.getTop() + ",y=" + child.getY());
        } else {
            //获取上一个MultiRecycleContainer容器,来确定当前child的位置 (当前child的位置为,上一个的top+上一个的height)
            int offset = previous.getTop() + previous.getHeight();
            child.offsetTopAndBottom(offset);
        }
        return true;
    }

    /**
     * 获取MultiRecycleContainer列表中的上一个
     * @param parent  CoordinatorLayout父容器
     * @param child  当前MultiRecycleContainer
     * @return
     */
    private MultiRecycleContainer getPreviousChild(CoordinatorLayout parent, MultiRecycleContainer child) {
        int cartindex = parent.indexOfChild(child);
        for (int i = cartindex - 1; i >= 0; i--) {
            View v = parent.getChildAt(i);
            if (v instanceof MultiRecycleContainer) {
                return (MultiRecycleContainer) v;
            }
        }
        return null;
    }

    boolean isVerticalScroll;//是否是垂直滚动
    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull MultiRecycleContainer child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        QDLogger.d("onStartNestedScroll axes=" + axes + ",target=" + target.getClass().getName());
        this.parent = coordinatorLayout;
        if (!isInital) {//初始化惯性滚动
            init(parent);
        }
        boolean isVertical = (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
        isVerticalScroll = isVertical && child == directTargetChild;//返回true则子view不在进行滚动
        return isVerticalScroll;
    }

    @Override
    public boolean onNestedPreFling(@NonNull CoordinatorLayout parent, @NonNull MultiRecycleContainer child, @NonNull View target, float velocityX, float velocityY) {
        //int shift = scroll(parent, child, (int) velocityY);
        // return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
        return false;
    }

    @Override
    public boolean onNestedFling(@NonNull CoordinatorLayout parent, @NonNull MultiRecycleContainer child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {
        QDLogger.d("onNestedFling...");
        //return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
        scroll(parent, child, consumed ? 0 : (int) velocityY);
        scrollParent(parent, child, null, consumed ? 0 : (int) velocityY);
        return false;
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout parent, @NonNull MultiRecycleContainer child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        QDLogger.d("dy=" + dy + ",consumed x=" + consumed[0] + ",y=" + consumed[1]);

        //dy>0上推,dy<0下拉
        boolean canScrollDown = target.canScrollVertically(-1);//的值表示是否能向下滚动
        boolean isToped = !canScrollDown;//表示是否已经滚动到顶部
        boolean canScrollUp = ViewCompat.canScrollVertically(target, 1);//的值表示是否能向上滚动
        boolean isBottomed = !canScrollUp;//表示已经滚动到底部
        QDLogger.d("isToped=" + isToped + ",isBottomed=" + isBottomed + ",child.getTop()=" + child.getTop());

        if (child.getTop() == 0 && ((dy > 0 && canScrollUp) || (dy < 0 && canScrollDown))) {
            consumed[1] = scrollChild(parent, child, target, dy);
        } else {
            consumed[1] = scrollParent(parent, child, target, dy);
        }
        //super.onNestedPreScroll(parent, child, target, dx, dy, consumed, type);
    }

    /**
     * 优先父view 消耗
     *
     * @param parent
     * @param target
     * @param dy
     * @return
     */
    private int scrollParent(CoordinatorLayout parent, MultiRecycleContainer child, View target, int dy) {
        int py = 0;
        QDLogger.d("获取下一个可滚动的视图");
        //1.获取下一个可滚动的视图
        if (dy > 0) {//上推
            QDLogger.d("上推");
            MultiRecycleContainer upper = getUpperChild(parent);
            QDLogger.d("获取下一个可上推的视图:" + upper);
            if (upper == null) {
                py = dy;
            } else {
                QDLogger.d("获取下一个可上推的视图:" + upper + ",dy=" + dy + ",upper.getTop()=" + upper.getTop());
                py = Math.min(upper.getTop(), dy);
            }
        } else {//下拉
            QDLogger.d("下拉");
            MultiRecycleContainer downer = getDownerChild(parent);//下一个
            QDLogger.d("获取下一个可下拉的视图:" + downer);
            if (downer == null) {
                py = dy;
            } else {
                QDLogger.d("获取下一个可下拉的视图dy=" + dy + ",downer.getTop()=" + downer.getTop());
                py = Math.max(downer.getTop(), dy);
            }
        }

        //2.scrollPrent
        scroll(parent, child, py);
        return py;
    }

    /**
     * 获取下一个即将要出现的可下拉滚动的控件
     *
     * @param parent
     * @return
     */
    private MultiRecycleContainer getDownerChild(CoordinatorLayout parent) {
        MultiRecycleContainer current = getCurrentChild(parent);
        int index = parent.indexOfChild(current);
        for (int i = index; i > 0; i--) {
            View v = parent.getChildAt(i);
            if (v instanceof MultiRecycleContainer) {
                boolean canScrollDown = (getChildRecyclerView((ViewGroup) v)).canScrollVertically(-1);//的值表示是否能向下滚动
                if (canScrollDown) {//下拉
                    return (MultiRecycleContainer) v;
                }
            }
        }
        return null;
    }

    /**
     * 获取下一个即将要出现的可上推滚动的控件
     *
     * @param parent
     * @return
     */
    private MultiRecycleContainer getUpperChild(CoordinatorLayout parent) {
        MultiRecycleContainer current = getCurrentChild(parent);
        int index = parent.indexOfChild(current);
        W:
        for (int i = index; i < parent.getChildCount(); i++) {
            View v = parent.getChildAt(i);
            if (v instanceof MultiRecycleContainer) {
                if (getChildRecyclerView((ViewGroup) v) == null) continue W;
                boolean canScrollUp = ViewCompat.canScrollVertically(getChildRecyclerView((ViewGroup) v), 1);//的值表示是否能向上滚动
                if (canScrollUp) {//可上推
                    return (MultiRecycleContainer) v;
                }
            }
        }
        return null;
    }

    /**
     * 获取child中的第一个recycleView,所以每个StackSlidingLayout中只能放一个recycleView
     *
     * @param v
     * @return
     */
    private RecyclerView getChildRecyclerView(ViewGroup v) {
        for (int i = 0; i < v.getChildCount(); i++) {
            if (v.getChildAt(i) instanceof RecyclerView) {
                return (RecyclerView) v.getChildAt(i);
            }
            if (v.getChildAt(i) instanceof ViewGroup) {
                RecyclerView recyclerView = findRecycleView((ViewGroup) v.getChildAt(i));
                if (recyclerView != null) {
                    return recyclerView;
                }
            }
        }
        return null;
    }

    /**
     * 获取当前子容器中的recycle,注意每个容器中只存放一个recyclerView
     * @param viewGroup
     * @return
     */
    private RecyclerView findRecycleView(ViewGroup viewGroup) {
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            if (viewGroup.getChildAt(i) instanceof RecyclerView) {
                return (RecyclerView) viewGroup.getChildAt(i);
            }
            if (viewGroup.getChildAt(i) instanceof ViewGroup) {
                RecyclerView recyclerView = findRecycleView((ViewGroup) viewGroup.getChildAt(i));
                if (recyclerView != null) {
                    return recyclerView;
                }
            }
        }
        return null;
    }

    /**
     * 优先子view滚动
     *
     * @param parent
     * @param target
     * @param dy
     * @return
     */
    private int scrollChild(CoordinatorLayout parent, MultiRecycleContainer child, View target, int dy) {
        //1.优先parent滑动
        //2.寻找到可以滚动的下一个child
        return 0;
    }

    @Override
    public void onNestedScroll(@NonNull CoordinatorLayout parent, @NonNull MultiRecycleContainer child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, @NonNull int[] consumed) {
        QDLogger.d("onNestedScroll...");

        //dy>0上推,dy<0下拉
        boolean canScrollDown = target.canScrollVertically(-1);//的值表示是否能向下滚动
        boolean isToped = !canScrollDown;//表示是否已经滚动到顶部
        boolean canScrollUp = ViewCompat.canScrollVertically(target, 1);//的值表示是否能向上滚动
        boolean isBottomed = !canScrollUp;//表示已经滚动到底部
        QDLogger.d("isToped=" + isToped + ",isBottomed=" + isBottomed + ",child.getTop()=" + child.getTop());

        if (child.getTop() == 0 && ((dyUnconsumed > 0 && canScrollUp) || (dyUnconsumed < 0 && canScrollDown))) {
            consumed[1] = scrollChild(parent, child, target, dyUnconsumed);
        } else {
            consumed[1] = scrollParent(parent, child, target, dyUnconsumed);
        }
    }

    private int scroll(CoordinatorLayout parent, MultiRecycleContainer child, int dy) {
        //1.k控制自己的移动
        int initialOffset = child.getTop();
        MultiRecycleContainer firstchild = getFirstChild(parent);
        MultiRecycleContainer lastchild = getLastChild(parent);

        //dy>0上推,dy<0下拉
        int top = firstchild.getTop();
        int bottom = lastchild.getBottom() - parent.getHeight();
        QDLogger.d("height=" + parent.getHeight() + ",lastchild.getBottom()=" + lastchild.getBottom() + ",firstchild.getTop()=" + firstchild.getTop());
        QDLogger.d("dy=" + dy + ",min=" + top + ",max=" + top + ",initialOffset - dy=" + (initialOffset - dy));
        int offset = 0;//= clamp(initialOffset - dy,top ,top)-initialOffset;
        //offset = 目标值-当前值
        if (dy > 0) {
            offset = -Math.min(dy, bottom);
        } else if (dy < 0) {
            offset = -Math.max(dy, top);
        }
        child.offsetTopAndBottom(offset);

        moveOther(parent, child, offset);
        return dy - offset;//滑动方向
    }

    /**
     * 获取下一个MultiRecycleContainer
     * @param parent
     * @param child
     * @return
     */
    private MultiRecycleContainer getNextChild(CoordinatorLayout parent, MultiRecycleContainer child) {
        int cartindex = parent.indexOfChild(child);
        for (int i = cartindex + 1; i < parent.getChildCount(); i++) {
            View v = parent.getChildAt(i);
            if (v instanceof MultiRecycleContainer) {
                return (MultiRecycleContainer) v;
            }
        }
        return null;
    }

    /**
     * 获取第一个MultiRecycleContainer
     * @param parent
     * @return
     */
    private MultiRecycleContainer getFirstChild(CoordinatorLayout parent) {
        MultiRecycleContainer firstchild = null;
        for (int i = 0; i < parent.getChildCount(); i++) {
            View view = parent.getChildAt(i);
            if (view instanceof MultiRecycleContainer) {
                firstchild = (MultiRecycleContainer) view;
                return firstchild;
            }
        }
        return firstchild;
    }
    /**
     * 获取当前MultiRecycleContainer
     * @param parent
     * @return
     */
    private MultiRecycleContainer getCurrentChild(CoordinatorLayout parent) {
        MultiRecycleContainer current = null;
        for (int i = 0; i < parent.getChildCount(); i++) {
            View view = parent.getChildAt(i);
            if (view.getTop() >= 0) {
                return (MultiRecycleContainer) view;
            }
        }
        return current;
    }
    /**
     * 获取最后一个MultiRecycleContainer
     * @param parent
     * @return
     */
    private MultiRecycleContainer getLastChild(CoordinatorLayout parent) {
        MultiRecycleContainer lastchild = null;
        for (int i = parent.getChildCount() - 1; i >= 0; i--) {
            View view = parent.getChildAt(i);
            if (view instanceof MultiRecycleContainer) {
                lastchild = (MultiRecycleContainer) view;
                return lastchild;
            }
        }
        return lastchild;
    }

    /**
     * 获取CoordinatorLayout所有子view视图高度
     * @param parent
     * @return
     */
    private int getAllChildHeight(CoordinatorLayout parent) {
        int height = 0;
        for (int i = 0; i < parent.getChildCount(); i++) {
            height += parent.getChildAt(i).getHeight();
        }
        return height;
    }

    /**
     * 移动其他平级的MultiRecycleContainer
     * @param parent
     * @param child
     * @param offset
     */
    private void moveOther(CoordinatorLayout parent, MultiRecycleContainer child, int offset) {
        MultiRecycleContainer current = child;
        MultiRecycleContainer above = getPreviousChild(parent, current);
        while (above != null) {
            int offset_c = current.getTop() - above.getHeight() - above.getTop();
            above.offsetTopAndBottom(offset_c);
            current = above;
            above = getPreviousChild(parent, current);
        }

        current = child;
        MultiRecycleContainer below = getNextChild(parent, current);
        while (below != null) {
            int offset_c = current.getTop() + current.getHeight() - below.getTop();
            below.offsetTopAndBottom(offset_c);
            current = below;
            below = getNextChild(parent, current);
        }
    }

    /****************   以下参考网上的惯性滚动   效果还不是很好     ****************************************************************/
    private boolean isInital;
    private void init(View view) {
        isInital = true;
        this.mContext = view.getContext();
        mViewFlinger = new ViewFlinger(mContext, view);
        final ViewConfiguration vc = ViewConfiguration.get(mContext);
        mTouchSlop = vc.getScaledTouchSlop();
        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
        DisplayMetrics metric = mContext.getResources().getDisplayMetrics();
        SCREEN_WIDTH = metric.widthPixels;
        SCREEN_HEIGHT = metric.heightPixels;
    }

    private Context mContext;
    private int SCREEN_WIDTH = 0;
    private int SCREEN_HEIGHT = 0;
    private int mWidth = 0;
    private int mHeight = 0;
    private static final int INVALID_POINTER = -1;
    public static final int SCROLL_STATE_IDLE = 0;
    public static final int SCROLL_STATE_DRAGGING = 1;
    public static final int SCROLL_STATE_SETTLING = 2;

    private int mScrollState = SCROLL_STATE_IDLE;
    private int mScrollPointerId = INVALID_POINTER;
    private int mLastTouchY;
    private int mTouchSlop;
    private int mMinFlingVelocity;
    private int mMaxFlingVelocity;
    private ViewFlinger mViewFlinger;
    private VelocityTracker mVelocityTracker;//滑动速度跟踪器

    @Override
    public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull MultiRecycleContainer child, @NonNull MotionEvent event) {

        if (!isInital) {
            init(parent);
        }
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        boolean eventAddedToVelocityTracker = false;
        final int action = MotionEventCompat.getActionMasked(event);
        final int actionIndex = MotionEventCompat.getActionIndex(event);
        final MotionEvent vtev = MotionEvent.obtain(event);

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                setScrollState(SCROLL_STATE_IDLE);
                mScrollPointerId = event.getPointerId(0);
                mLastTouchY = (int) (event.getY() + 0.5f);
                break;
            }
            case MotionEventCompat.ACTION_POINTER_DOWN: {
                mScrollPointerId = event.getPointerId(actionIndex);
                mLastTouchY = (int) (event.getY(actionIndex) + 0.5f);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                final int index = event.findPointerIndex(mScrollPointerId);
                if (index < 0) {
                    Log.e("zhufeng", "Error processing scroll; pointer index for id " + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
                    return false;
                }

                final int y = (int) (event.getY(index) + 0.5f);
                int dy = mLastTouchY - y;

                if (mScrollState != SCROLL_STATE_DRAGGING) {
                    boolean startScroll = false;

                    if (Math.abs(dy) > mTouchSlop) {
                        if (dy > 0) {
                            dy -= mTouchSlop;
                        } else {
                            dy += mTouchSlop;
                        }
                        startScroll = true;
                    }
                    if (startScroll) {
                        setScrollState(SCROLL_STATE_DRAGGING);
                    }
                }

                if (mScrollState == SCROLL_STATE_DRAGGING) {
                    mLastTouchY = y;
                    constrainScrollBy(dy);
                }
                break;
            }
            case MotionEventCompat.ACTION_POINTER_UP: {
                if (event.getPointerId(actionIndex) == mScrollPointerId) {
                    // Pick a new pointer to pick up the slack.
                    final int newIndex = actionIndex == 0 ? 1 : 0;
                    mScrollPointerId = event.getPointerId(newIndex);
                    mLastTouchY = (int) (event.getY(newIndex) + 0.5f);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                mVelocityTracker.addMovement(vtev);
                eventAddedToVelocityTracker = true;
                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
                float yVelocity = -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId);
                Log.i("zhufeng", "速度取值:" + yVelocity);
                if (Math.abs(yVelocity) < mMinFlingVelocity) {
                    yVelocity = 0F;
                } else {
                    yVelocity = Math.max(-mMaxFlingVelocity, Math.min(yVelocity, mMaxFlingVelocity));
                }
                if (yVelocity != 0) {
                    mViewFlinger.fling((int) yVelocity);
                } else {
                    setScrollState(SCROLL_STATE_IDLE);
                }
                resetTouch();
                break;
            }
            case MotionEvent.ACTION_CANCEL: {
                resetTouch();
                break;
            }
        }
        if (!eventAddedToVelocityTracker) {
            mVelocityTracker.addMovement(vtev);
        }
        vtev.recycle();
        return true;
        //return super.onTouchEvent(parent, child, ev);
    }

    private void resetTouch() {
        if (mVelocityTracker != null) {
            mVelocityTracker.clear();
        }
    }

    private void setScrollState(int state) {
        if (state == mScrollState) {
            return;
        }
        mScrollState = state;
        if (state != SCROLL_STATE_SETTLING) {
            mViewFlinger.stop();
        }
    }

    private class ViewFlinger implements Runnable {
        private int mLastFlingY = 0;
        private OverScroller mScroller;
        private boolean mEatRunOnAnimationRequest = false;
        private boolean mReSchedulePostAnimationCallback = false;
        private View mView;

        public ViewFlinger(Context context, View view) {
            this.mView = view;
            mScroller = new OverScroller(context, sQuinticInterpolator);
        }

        @Override
        public void run() {
            disableRunOnAnimationRequests();
            final OverScroller scroller = mScroller;
            if (scroller.computeScrollOffset()) {
                final int y = scroller.getCurrY();
                int dy = y - mLastFlingY;
                mLastFlingY = y;
                constrainScrollBy(dy);
                postOnAnimation();
            }
            enableRunOnAnimationRequests();
        }

        public void fling(int velocityY) {
            mLastFlingY = 0;
            setScrollState(SCROLL_STATE_SETTLING);
            mScroller.fling(0, 0, 0, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
            postOnAnimation();
        }

        public void stop() {
            mView.removeCallbacks(this);
            mScroller.abortAnimation();
        }

        private void disableRunOnAnimationRequests() {
            mReSchedulePostAnimationCallback = false;
            mEatRunOnAnimationRequest = true;
        }

        private void enableRunOnAnimationRequests() {
            mEatRunOnAnimationRequest = false;
            if (mReSchedulePostAnimationCallback) {
                postOnAnimation();
            }
        }

        void postOnAnimation() {
            if (mEatRunOnAnimationRequest) {
                mReSchedulePostAnimationCallback = true;
            } else {
                mView.removeCallbacks(this);
                ViewCompat.postOnAnimation(mView, this);
            }
        }

    }

    private void constrainScrollBy(int dy) {
       /* Rect viewport = new Rect();
        getGlobalVisibleRect(viewport);
        int height = viewport.height();
        int width = viewport.width();*/
        if (parent == null) return;
        int height = getAllChildHeight(parent);
        int width = parent.getWidth();
        QDLogger.d("height=" + height + ",width=" + width);

        int scrollY = getFirstChild(parent).getTop();
        MultiRecycleContainer child = getCurrentChild(parent);

        //下边界
        if (mHeight - scrollY - dy < height) {
            dy = mHeight - scrollY - height;
        }
        //上边界
        if (scrollY + dy < 0) {
            dy = -scrollY;
        }
        // scroll(parent, child, dy);
        scrollParent(parent, child, null, dy);
    }

    //f(x) = (x-1)^5 + 1
    private static final Interpolator sQuinticInterpolator = new Interpolator() {
        @Override
        public float getInterpolation(float t) {
            t -= 1.0f;
            return t * t * t * t * t + 1.0f;
        }
    };
}

Behavior部分负责管理多个容器的位置,并且负责协调滚动事件

使用部分

1.xml布局


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white">
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/transparent_light_77">

        <cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer
            android:id="@+id/ssl_01"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:colorBackground="@color/red"
            android:text="RecycleView A">

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <LinearLayout
                    android:id="@+id/ll_header_A"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/red"
                    android:orientation="horizontal"
                    android:paddingLeft="16dp"
                    android:paddingTop="16dp"
                    android:paddingBottom="36dp">

                    <ImageView
                        android:layout_width="45dp"
                        android:layout_height="45dp"
                        android:padding="5dp"
                        android:src="@drawable/umeng_socialize_sina" />

                    <TextView
                        android:id="@+id/header_A"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:paddingLeft="15dp"
                        android:text="斗气大陆"
                        android:textColor="@color/white"
                        android:textSize="32dp" />
                LinearLayout>

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/list_A"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:overScrollMode="never">androidx.recyclerview.widget.RecyclerView>

            LinearLayout>

        cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer>

        <cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer
            android:id="@+id/ssl_02"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:colorBackground="@color/green"
            android:text="RecycleView B">

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <LinearLayout
                    android:id="@+id/ll_header_B"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/green"
                    android:orientation="horizontal"
                    android:padding="6dp">

                    <ImageView
                        android:layout_width="55dp"
                        android:layout_height="55dp"
                        android:padding="5dp"
                        android:src="@drawable/shield" />

                    <TextView
                        android:id="@+id/header_B"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:gravity="center_vertical"
                        android:paddingLeft="10dp"
                        android:text="再接再厉"
                        android:textColor="@color/white"
                        android:textSize="22dp" />
                LinearLayout>

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/list_B"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:overScrollMode="never">

                androidx.recyclerview.widget.RecyclerView>

            LinearLayout>
        cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer>

        <cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer
            android:id="@+id/ssl_03"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:colorBackground="@color/yellow"
            android:text="RecycleView C">

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <LinearLayout
                    android:id="@+id/ll_header_C"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/blue"
                    android:orientation="horizontal"
                    android:padding="6dp">

                    <TextView
                        android:id="@+id/header_C"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:gravity="center_vertical"
                        android:paddingLeft="15dp"
                        android:text="人生如梦"
                        android:textColor="@color/white"
                        android:textSize="16dp"
                        android:textStyle="bold" />

                    <ImageView
                        android:layout_width="35dp"
                        android:layout_height="35dp"
                        android:padding="5dp"
                        android:src="@drawable/umeng_socialize_wxcircle" />
                LinearLayout>

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/list_C"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:overScrollMode="never">

                androidx.recyclerview.widget.RecyclerView>

            LinearLayout>
        cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer>

        <cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer
            android:id="@+id/ssl_04"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:colorBackground="@color/blue"
            android:text="RecycleView D">

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <LinearLayout
                    android:id="@+id/ll_header_D"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/yellow"
                    android:orientation="horizontal"
                    android:padding="6dp">

                    <ImageView
                        android:layout_width="65dp"
                        android:layout_height="65dp"
                        android:padding="5dp"
                        android:src="@drawable/umeng_socialize_fav" />

                    <TextView
                        android:id="@+id/header_D"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:gravity="center_vertical"
                        android:paddingLeft="15dp"
                        android:text="艳阳高照"
                        android:textColor="@color/black"
                        android:textSize="28dp" />
                LinearLayout>

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/list_D"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:overScrollMode="never">

                androidx.recyclerview.widget.RecyclerView>

            LinearLayout>
        cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer>

        <cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer
            android:id="@+id/ssl_05"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:colorBackground="@color/chocolate"
            android:text="RecycleView E">

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <LinearLayout
                    android:id="@+id/ll_header_E"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/gray"
                    android:orientation="horizontal"
                    android:padding="6dp">

                    <TextView
                        android:id="@+id/header_E"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:drawableLeft="@drawable/umeng_socialize_qzone"
                        android:gravity="center_vertical"
                        android:paddingLeft="15dp"
                        android:text="过眼云烟"
                        android:textColor="@color/white"
                        android:textSize="22dp"
                        android:textStyle="italic" />
                LinearLayout>

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/list_E"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:overScrollMode="never">

                androidx.recyclerview.widget.RecyclerView>

            LinearLayout>
        cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.MultiRecycleContainer>


    androidx.coordinatorlayout.widget.CoordinatorLayout>
FrameLayout>

demo里用了5个recyclerView,看起来代码很多。其实就是CoordinatorLayout里包含多个MultiRecycleContainer

 	
	 	
	 		
	 		<其他布局/>
	 	
  	
  	
  	
	 	
	 		
	 		<其他布局/>
	 	
  	

2.java部分

package cn.demomaster.huan.quickdevelop.fragment.component;

import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

import cn.demomaster.huan.quickdevelop.R;
import cn.demomaster.huan.quickdeveloplibrary.annotation.ActivityPager;
import cn.demomaster.huan.quickdeveloplibrary.annotation.ResType;
import cn.demomaster.huan.quickdeveloplibrary.base.fragment.QDBaseFragment;
import cn.demomaster.huan.quickdeveloplibrary.base.tool.actionbar.ActionBarInterface;
import cn.demomaster.huan.quickdeveloplibrary.view.loading.StateView;
import cn.demomaster.huan.quickdeveloplibrary.widget.stackslidingLayout.ComponentAdapter;


/**
 * Squirrel桓
 * 2018/8/25
 */

@ActivityPager(name = "StackSliding", preViewClass = StateView.class, resType = ResType.Custome)
public class StackSlidingLayoutFragment extends QDBaseFragment {


    //Components
    ViewGroup mView;

    @Override
    public int getBackgroundColor() {
        return Color.WHITE;
    }

    @Override
    public ViewGroup getContentView(LayoutInflater inflater) {
        if (mView == null) {
            mView = (ViewGroup) inflater.inflate(R.layout.fragment_layout_stack_sliding, null);
        }
        return mView;
    }


    private RecyclerView recyclerView_A, recyclerView_B, recyclerView_C, recyclerView_D, recyclerView_E;
    private TextView header_A, header_B, header_C, header_D, header_E;
    private ComponentAdapter adapter_A, adapter_B, adapter_C, adapter_D, adapter_E;
    private List<String> items_A, items_B, items_C, items_D, items_E;

    @Override
    public void initView(View rootView, ActionBarInterface actionBarLayout) {
        actionBarLayout.setActionBarType(ActionBarInterface.ACTIONBAR_TYPE.ACTION_TRANSPARENT);
        actionBarLayout.setHeaderBackgroundColor(Color.TRANSPARENT);

        //A
        recyclerView_A = rootView.findViewById(R.id.list_A);
        header_A = rootView.findViewById(R.id.header_A);
        adapter_A = new ComponentAdapter(getContext(), Color.BLACK);
        items_A = new ArrayList();
        int c = 20;
        for (int i = 0; i < c; i++) {
            items_A.add("A" + i);
        }
        adapter_A.updateList(items_A);
        recyclerView_A.setAdapter(adapter_A);
        recyclerView_A.setLayoutManager(new LinearLayoutManager(getContext()));

        //B
        recyclerView_B = rootView.findViewById(R.id.list_B);
        header_B = rootView.findViewById(R.id.header_B);
        adapter_B = new ComponentAdapter(getContext(), Color.BLACK);
        items_B = new ArrayList();
        c = 6;
        for (int i = 0; i < c; i++) {
            items_B.add("B" + i);
        }
        adapter_B.updateList(items_B);
        recyclerView_B.setAdapter(adapter_B);
        recyclerView_B.setLayoutManager(new LinearLayoutManager(getContext()));

        //C
        recyclerView_C = rootView.findViewById(R.id.list_C);
        header_C = rootView.findViewById(R.id.header_C);
        adapter_C = new ComponentAdapter(getContext(), Color.BLACK);
        items_C = new ArrayList();
        c = 33;
        for (int i = 0; i < c; i++) {
            items_C.add("C" + i);
        }
        adapter_C.updateList(items_C);
        recyclerView_C.setAdapter(adapter_C);
        recyclerView_C.setLayoutManager(new LinearLayoutManager(getContext()));

        //D
        recyclerView_D = rootView.findViewById(R.id.list_D);
        header_D = rootView.findViewById(R.id.header_D);
        adapter_D = new ComponentAdapter(getContext(), Color.BLACK);
        items_D = new ArrayList();
        c = 3;
        for (int i = 0; i < c; i++) {
            items_D.add("D" + i);
        }
        adapter_D.updateList(items_D);
        recyclerView_D.setAdapter(adapter_D);
        recyclerView_D.setLayoutManager(new LinearLayoutManager(getContext()));

        //E
        recyclerView_E = rootView.findViewById(R.id.list_E);
        header_E = rootView.findViewById(R.id.header_E);
        adapter_E = new ComponentAdapter(getContext(), Color.BLACK);
        items_E = new ArrayList();
        c = 16;
        for (int i = 0; i < c; i++) {
            items_E.add("E" + i);
        }
        adapter_E.updateList(items_E);
        recyclerView_E.setAdapter(adapter_E);
        recyclerView_E.setLayoutManager(new LinearLayoutManager(getContext()));
    }

    @Override
    public void onStop() {
        super.onStop();
    }
}

这部分没什么可说的,正常初始化使用recycleView和其他自定义布局。

遗留

每次最遗憾的是留了一个小尾巴,个人能力有限。1.使用网上的惯性滚动代码,效果不是很理想。2.惯性滑动跨越要把滑动事件传动给上一个recycler继续滑动,还没有处理好。

也是首次使用behavior做个记录,后面还想写写更多有意思的效果。

我的项目

allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}
dependencies {
	        implementation 'com.github.squirrelhuan:QuickDevelop:1.1.23'
	}

项目地址:https://github.com/squirrelhuan/QuickDevelop

你可能感兴趣的:(个人原创,Android,Behavior,多RecyclerView处理)