RecyclerView嵌套聯(lián)動

/**

* RecyclerView -ViewPage-RecyclerView? 內(nèi)層嵌套的RV,? 解決上下滑動沖突

*/

public class ParentRecyclerViewextends RecyclerView {

int totalDy =0;

/**

* 用于判斷RecyclerView是否在fling

*/

? ? boolean isStartFling =false;

private int mMaxDistance;

private FlingHelper mFlingHelper;

/**

* 記錄上次Event事件的y坐標(biāo)

*/

? ? private float lastY;

public int getVelocityY() {

return velocityY;

}

/**

* 記錄當(dāng)前滑動的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過程中已經(jīng)到底部,需要讓子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情況下,記錄當(dāng)前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);

}

/****

* 滑動距離及坐標(biāo) 歸還父控件焦點

****/

? ? 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表示已經(jīng)滾動到底部

? ? ? ? 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:當(dāng)ChildRecyclerView下滑時(手指未放開),ChildRecyclerView滑動到頂部(非fling),此時ParentRecyclerView不會繼續(xù)下滑。

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

? ? @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.當(dāng)前Parent RecyclerView沒有滑動底,且dy> 0 是下滑

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

//2.當(dāng)前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:當(dāng)ChildRecyclerView下滑時(手指未放開),ChildRecyclerView滑動到頂部(非fling),此時ParentRecyclerView不會繼續(xù)下滑。

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

}



/**

* RecyclerView -ViewPage-RecyclerView? 內(nèi)層嵌套的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) {

//當(dāng)前ChildRecyclerView已經(jīng)滑動到頂部,且豎直方向加速度不為0,如果有多余的需要交由父RecyclerView繼續(xù)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坐標(biāo)

? ? private float downY;//按下時 的Y坐標(biāo)

? ? @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);

//將按下時的坐標(biāo)存儲

? ? ? ? ? ? ? ? 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表示已經(jīng)滾動到頂部

? ? ? ? return !canScrollVertically(-1);

}

public boolean isScrollEnd() {

//RecyclerView.canScrollVertically(1)的值表示是否能向上滾動,false表示已經(jīng)滾動到底部

? ? ? ? return !canScrollVertically(1);

}

private ParentRecyclerView findParentRecyclerView() {

ViewParent parentView = getParent();

while (!(parentViewinstanceof ParentRecyclerView)) {

parentView = parentView.getParent();

}

return (ParentRecyclerView) parentView;

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容