1. 程式人生 > >AppBarLayout快速滑動導致回彈的解決方案CollapsingToolbarLayout

AppBarLayout快速滑動導致回彈的解決方案CollapsingToolbarLayout

在26版本的sdk上,谷歌解決了之前存在已久的一個問題:AppBarLayout、CollapsingToolbarLayout和RecyclerView共存時,無法通過fling快速展開AppBarLayout

但是隨之而來的是一個新問題,當快速上下滾動,最後回到頂部時,AppBarLayout會出現回彈(bounce)的現象

原因是內部的非touch fling還未結束導致的

目前的一個解決方法是在非touch時block掉fling事件

程式碼如下:

[java] view plain copy print?
  1. publicclass TestBehavior extends AppBarLayout.Behavior {  
  2.     privatestaticfinalint TYPE_FLING = 1;  
  3.     privateboolean isFlinging;  
  4.     privateboolean shouldBlockNestedScroll;  
  5.     public TestBehavior() {  
  6.     }  
  7.     public TestBehavior(Context context, AttributeSet attrs) {  
  8.         super(context, attrs);  
  9.     }  
  10.     @Override
  11.     publicboolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {  
  12.         Log.i(”TestBehavior”“onInterceptTouchEvent:” + child.getTotalScrollRange());  
  13.         shouldBlockNestedScroll = false;  
  14.         if (isFlinging) {  
  15.             shouldBlockNestedScroll = true;  
  16.         }  
  17.         returnsuper.onInterceptTouchEvent(parent, child, ev);  
  18.     }  
  19.     @Override
  20.     public
    void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {  
  21.         //注意看ViewCompat.TYPE_TOUCH
  22.         Log.i(”TestBehavior”“onNestedPreScroll:” + child.getTotalScrollRange() + “ ,dx:” + dx + “ ,dy:” + dy + “ ,type:” + type);  
  23.         //返回1時,表示當前target處於非touch的滑動,
  24.         //該bug的引起是因為appbar在滑動時,CoordinatorLayout內的實現NestedScrollingChild2介面的滑動子類還未結束其自身的fling
  25.         //所以這裡監聽子類的非touch時的滑動,然後block掉滑動事件傳遞給AppBarLayout
  26.         if (type == TYPE_FLING) {  
  27.             isFlinging = true;  
  28.         }  
  29.         if (!shouldBlockNestedScroll) {  
  30.             super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);  
  31.         }  
  32.     }  
  33.     @Override
  34.     publicvoid onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int
  35.             dxUnconsumed, int dyUnconsumed, int type) {  
  36.         Log.i(”TestBehavior”“onNestedScroll: target:” + target.getClass() + “ ,” + child.getTotalScrollRange() + “ ,dxConsumed:”
  37.                 + dxConsumed + ” ,dyConsumed:” + dyConsumed + “ ” + “,type:” + type);  
  38.         if (!shouldBlockNestedScroll) {  
  39.             super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);  
  40.         }  
  41.     }  
  42.     @Override
  43.     publicvoid onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {  
  44.         super.onStopNestedScroll(coordinatorLayout, abl, target, type);  
  45.         isFlinging = false;  
  46.         shouldBlockNestedScroll = false;  
  47.     }  
  48. }  
public class TestBehavior extends AppBarLayout.Behavior {

    private static final int TYPE_FLING = 1;

    private boolean isFlinging;
    private boolean shouldBlockNestedScroll;

    public TestBehavior() {
    }

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

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
        Log.i("TestBehavior", "onInterceptTouchEvent:" + child.getTotalScrollRange());
        shouldBlockNestedScroll = false;
        if (isFlinging) {
            shouldBlockNestedScroll = true;
        }
        return super.onInterceptTouchEvent(parent, child, ev);
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
        //注意看ViewCompat.TYPE_TOUCH
        Log.i("TestBehavior", "onNestedPreScroll:" + child.getTotalScrollRange() + " ,dx:" + dx + " ,dy:" + dy + " ,type:" + type);

        //返回1時,表示當前target處於非touch的滑動,
        //該bug的引起是因為appbar在滑動時,CoordinatorLayout內的實現NestedScrollingChild2介面的滑動子類還未結束其自身的fling
        //所以這裡監聽子類的非touch時的滑動,然後block掉滑動事件傳遞給AppBarLayout
        if (type == TYPE_FLING) {
            isFlinging = true;
        }
        if (!shouldBlockNestedScroll) {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
        }
    }

    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int
            dxUnconsumed, int dyUnconsumed, int type) {
        Log.i("TestBehavior", "onNestedScroll: target:" + target.getClass() + " ," + child.getTotalScrollRange() + " ,dxConsumed:"
                + dxConsumed + " ,dyConsumed:" + dyConsumed + " " + ",type:" + type);
        if (!shouldBlockNestedScroll) {
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
        }
    }

    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {
        super.onStopNestedScroll(coordinatorLayout, abl, target, type);
        isFlinging = false;
        shouldBlockNestedScroll = false;
    }
}
在AppBarLayout的佈局裡使用這個Behavior即可