Android原生开发多个RecyclerView
的复杂界,如淘宝首页这样的界面。不可避免的遇到滑动冲突的问题,官方为我们提供了NestedScrollView可以很完美的解决滑动问题。这里记录一下在没有了解NestedScrollView
源码的情况下,自己采用behavior
的解决思路。
1.自定义RecyclerView容器(只包含一个RecyclerView可以添加其他布局)
2.把自定义容器部署到CoordinatorLayout内
2.使用Behavior统一管理自定义容器(位置及滑动事件)
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并且可以添加其他自定义的布局
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部分负责管理多个容器的位置,并且负责协调滚动事件
<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
<其他布局/>
<其他布局/>
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