1. 程式人生 > >CoordinatorLayout+AppBarLayout+RecyclerView 滑動衝突引發螢幕抖動

CoordinatorLayout+AppBarLayout+RecyclerView 滑動衝突引發螢幕抖動

專案示例圖 
這裡寫圖片描述 
出現的問題連結: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; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

看到了吧 在這個類中賦予了滑動屬性。 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 : [email protected]
 *
 * @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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

使用位置是在佈局檔案的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">
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

結尾

大工告成, game over !


轉載至:https://blog.csdn.net/a940659387/article/details/80136852