CoordinatorLayout 學習(三) - 經過自定義Behavior解決AppBarLayout 不能Fling的問題

  AppBarLayout有一個很是噁心的設計,就是將自身的Fling徹底內部消化,從而致使了RecyclerView之類的控件不能Fling。本文打算採用自定義Behavior的方法來解決該問題,應該是現在網上最簡單的方法。   我在思考本身的解決方法以前,在網上簡單的搜索一番前人的答案,發現已知的答案都很是的麻煩,好多的方法都是將AppBarLayout相關代碼拷貝出來,而後簡單修改,工做量很是的大和複雜。本文將介紹做者本人思考出來的一種方法,本文的好處就是:你只定義兩個Behavior就能實現。bash

1. 問題的緣由

  在解決的問題,咱們先來看一下這個問題產生的緣由,問題的緣由在HeaderBehavior裏面有一段代碼:ide

case MotionEvent.ACTION_UP:
                if (mVelocityTracker != null) {
                    mVelocityTracker.addMovement(ev);
                    mVelocityTracker.computeCurrentVelocity(1, 0.01f);
                    mVelocityTracker.computeCurrentVelocity(1000);

                    float yvel = mVelocityTracker.getYVelocity(mActivePointerId);
                    fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel);
                }
複製代碼

  fling的範圍是[ -getScrollRangeForDragFling(child), 0],這就致使了AppBarLayoutFling到頂部就當即中止,緣由的就是這樣,很是的簡單。post

2. 解決問題

  咱們要想解決這個問題,就要從根本入手。既然Fling不能傳遞出去致使的這個問題,咱們就將它傳遞出去,如何傳遞出去呢?本文采用嵌套滑動方式傳遞。ui

(1).自定義AppBarLayout的Behavior

  AppBarLayoutBehavior關鍵在於怎麼將Fling傳遞出去,咱們來看看,主要分爲三步:this

  1. HeaderBehavior中,將onTouchEvent方法及其相關方法代碼拷貝出來
  2. AppBarLayoutFling時,調用咱們本身的fling方法(這裏雖然說是本身的fing方法,其實就是將原來的fling方法代碼拷貝出來)。
  3. 改寫FlingRunnable,使Fling事件能順利傳遞出去。

  在這其中,咱們須要注意一點:spa

  1. 調用fling方法進行fling時,minOffset傳爲Integer.MIN_VALUE,這樣這個Fling事件就能夠肆意妄爲了,不用侷限於某一個範圍。
@Override
    public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
        // ······
            case MotionEvent.ACTION_UP:
                if (velocityTracker != null) {
                    velocityTracker.addMovement(ev);
                    velocityTracker.computeCurrentVelocity(1000);
                    float yvel = velocityTracker.getYVelocity(activePointerId);
                    flingV2(parent, child, Integer.MIN_VALUE, 0, yvel);
                }
        // ······
    }
複製代碼

  如上的代碼就是咱們改寫了的部分,其中flingV2方法就是咱們自定義的fling方法,代碼與原來的fling方法如出一轍。   接下來改寫FlingRunnable類,咱們來看看:設計

private class FlingRunnable implements Runnable {
        private final CoordinatorLayout parent;
        private final AppBarLayout layout;

        FlingRunnable(CoordinatorLayout parent, AppBarLayout layout) {
            this.parent = parent;
            this.layout = layout;
        }

        @Override
        public void run() {
            if (layout != null && scroller != null) {
                if (scroller.computeScrollOffset()) {
                    scroll();
                } else {
                    onFlingFinished(parent, layout);
                }
            }
        }

        private void scroll() {
            // 若是AppBarLayout滑到閾值,此時須要將fling事件傳遞下去
            if (scroller.getCurrY() <= -getScrollRangeForDragFling(layout) && scroller.getStartX() > scroller.getFinalY()) {
                setHeaderTopBottomOffset(parent, layout, -getScrollRangeForDragFling(layout));
                if (layout.startNestedScroll(View.SCROLL_AXIS_VERTICAL)) {
                    layout.dispatchNestedFling(scroller.getCurrVelocity(), scroller.getCurrVelocity(), false);
                }
                scroller.forceFinished(true);
            } else { // 若是AppBarLayout尚未滑到閾值,就讓開心的滑動。
                setHeaderTopBottomOffset(parent, layout, scroller.getCurrY());
            }
            ViewCompat.postOnAnimation(layout, this);
        }
    }
複製代碼

  AppBarLayout自定義Behavior流程到此結束了,接下來咱們看一下RecyclerViewBehaviorcode

(2). 自定義RecyclerView的Behavior

  代碼以下:cdn

public class FlingScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {


    public FlingScrollingViewBehavior() {
    }

    public FlingScrollingViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        return true;
    }

    @Override
    public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {
        if (child != target) {
            ((RecyclerView) (child)).fling((int) velocityX, (int) velocityY);
        }
        return true;
    }
}
複製代碼

  而後咱們在xml中將Behavior進行綁定,你就發現,AppBarLayout能夠Fling了,效果以下: xml

3. 總結

   本方法很是的簡單,到此就結束了,不像網上其餘方法那樣長篇大論。在這裏,我對本文作一個簡單的總結。

  1. 自定義AppBarLayout的Behavior,這其中必定保證把Fling事件傳遞出去。
  2. 自定義RecyclerView的Behavior中,能夠經過RecyclerView的fling方法進行fling。

  原本上傳源碼供你們參考的,可是介於一部分的我的緣由,暫時不能上傳源碼,實在抱歉!後續我會補上的,有想要源碼的同窗能夠先加個人QQ:2196995023。

相關文章
相關標籤/搜索