CoordinatorLayout+AppBarLayout+RecyclerView 滑动冲突引发屏幕抖动

xiaoxiao2021-02-28  108

项目示例图  出现的问题链接:https://ask.csdn.net/questions/363070 跟这个哥们遇到的问题一样一样的;

原因

用到的布局结构就是CoordinatorLayout+AppBarLayout+ViewPager 然后Viewpager里是两个RecyclerView;

当你appbar高度低的时候一般不会触发这个问题,因为appbar fling 豪无用武之地。 只有你的appbar的高度到达一定的程度,那么问题就出来。

So You know !! 原因是什么了吧。其实就是appbar 有向下滑动的事件 ,然后你recylerView有向上滑动的事件,两个事件冲突,你往下挪动一下,我往上挪动一下,沙卡拉卡一会。

解决

问题出来首先要找根源,recyclerview 跟appbar 关联的桥梁是什么, Behavior ,具体说是 AppBarLayout.Behavior

但是这个类中并么有处理滑动的东东,so 向上刨根问题。 看他的父类HeaderBehavior

@Override public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) { if (mTouchSlop < 0) { mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop(); } switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: { final int x = (int) ev.getX(); final int y = (int) ev.getY(); if (parent.isPointInChildBounds(child, x, y) && canDragView(child)) { mLastMotionY = y; mActivePointerId = ev.getPointerId(0); ensureVelocityTracker(); } else { return false; } break; } case MotionEvent.ACTION_MOVE: { final int activePointerIndex = ev.findPointerIndex(mActivePointerId); if (activePointerIndex == -1) { return false; } final int y = (int) ev.getY(activePointerIndex); int dy = mLastMotionY - y; if (!mIsBeingDragged && Math.abs(dy) > mTouchSlop) { mIsBeingDragged = true; if (dy > 0) { dy -= mTouchSlop; } else { dy += mTouchSlop; } } if (mIsBeingDragged) { mLastMotionY = y; // We're being dragged so scroll the ABL scroll(parent, child, dy, getMaxDragOffset(child), 0); } break; } case MotionEvent.ACTION_UP: if (mVelocityTracker != null) { mVelocityTracker.addMovement(ev); mVelocityTracker.computeCurrentVelocity(1000); float yvel = mVelocityTracker.getYVelocity(mActivePointerId); fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel); } // $FALLTHROUGH case MotionEvent.ACTION_CANCEL: { mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; } } if (mVelocityTracker != null) { mVelocityTracker.addMovement(ev); } return true; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172

看到了吧 在这个类中赋予了滑动属性。 fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel); 这个就是问题出现的原因, fling 还在进行 你就开始向上滑动,肯定有问题啊。

解决办法跟NestedScrolling机制 有关。

不懂这套机制的可以参考这篇文章:https://blog.csdn.net/lmj623565791/article/details/52204039

简单说呢 就是recyclerView滑动前会触发onNestedPreScroll 方法 告诉他爸爸 让他爸爸知道他要开始滑动了,so我们在这个里做操作直接结束Appbar的滑动就行。

代码

import android.content.Context; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.OverScroller; import java.lang.reflect.Field; /** * Created by 于德海 on 2018/4/27. * package inter.baisong.widgets * email : yudehai0204@163.com * * @describe 自定义behavior 以解决滑动抖动 * */ public class CustomBehavior extends AppBarLayout.Behavior { private OverScroller mScroller; public CustomBehavior() { } public CustomBehavior(Context context, AttributeSet attrs) { super(context, attrs); getParentScroller(context); } /** * 反射获得滑动属性。 * * @param context */ private void getParentScroller(Context context) { if (mScroller != null) return; mScroller = new OverScroller(context); try { Class<?> reflex_class = getClass().getSuperclass().getSuperclass();//父类AppBarLayout.Behavior 父类的父类 HeaderBehavior Field fieldScroller = reflex_class.getDeclaredField("mScroller"); fieldScroller.setAccessible(true); fieldScroller.set(this, mScroller); } catch (Exception e) {} } //fling上滑appbar然后迅速fling下滑recycler时, HeaderBehavior的mScroller并未停止, 会导致上下来回晃动 @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) { if(mScroller!=null){ //当recyclerView 做好滑动准备的时候 直接干掉Appbar的滑动 if (mScroller.computeScrollOffset()) { mScroller.abortAnimation(); } } if (type == ViewCompat.TYPE_NON_TOUCH&&getTopAndBottomOffset() == 0) { //recyclerview 鸡儿的 惯性比较大 会顶在头部一会儿 到头直接干掉它的滑动 ViewCompat.stopNestedScroll(target, type); } super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type); } @Override public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent e) { switch (e.getActionMasked()) { case MotionEvent.ACTION_DOWN: break; } return super.onTouchEvent(parent,child,e); } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273

使用位置是在布局文件的appbarlayout中加behavior

示例:

<android.support.design.widget.AppBarLayout android:id="@+id/mAppbar" app:elevation="0dip" app:layout_behavior="inter.****.widgets.CustomBehavior" android:layout_width="match_parent" android:layout_height="wrap_content"> 123456

结尾

大工告成, game over !

转载至:https://blog.csdn.net/a940659387/article/details/80136852

转载请注明原文地址: https://www.6miu.com/read-2625601.html

最新回复(0)