天天看點

android coordinatorlayout滑動卡頓,CoordinatorLayout和AppBarLayout滑動彈跳(回彈)問題解決...

前言:項目中在開發吸頂布局的時候使用了CoordinatorLayout和AppBarLayout實作,布局包括CoordinatorLayout、CollapsingToolbarLayout 、AppBarLayout、ToolBar和RecyclerView這五個類,

但是給CollapsingToolbarLayout 添加了app:layout_scrollFlags=snap屬性之後,輕輕滑動的時候在snap閥值的地方會有一個卡頓的過程然後會繼續滑動到結束。

1.首先看下xml布局檔案:

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/AppFragment_AppBarLayout"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

app:layout_behavior="com.boco.whl.funddemo.module.adapter.behavior.WhlBehavior">

android:id="@+id/AppFragment_CollapsingToolbarLayout"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:contentScrim="@color/colorPrimary"

app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

android:layout_width="match_parent"

android:layout_height="match_parent"

app:layout_collapseMode="parallax"

app:layout_collapseParallaxMultiplier="0.7">

android:id="@+id/AppFragment_Toolbar"

android:layout_width="match_parent"

android:layout_height="76dp"

android:paddingTop="20dp"

app:layout_collapseMode="pin">

android:layout_width="match_parent"

android:layout_height="56dp">

android:id="@+id/MyFragment_recyclerView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginBottom="20dp"

android:background="@android:color/white"

app:layout_behavior="@string/appbar_scrolling_view_behavior" />

重點是

1.AppBarLayout中增加如下命名空間:

app:layout_behavior="com.boco.whl.funddemo.module.adapter.behavior.WhlBehavior";

用來解決滑動回彈的bug;

2.CollapsingToolBarLayout中增加如下命名空間:

app:layout_scrollFlags="scroll|exitUntilCollapsed|snap";

(5個scrollFlag關聯源碼)

* The view will be scroll in direct relation to scroll events. This flag needs to be

* set for any of the other flags to take effect. If any sibling views

* before this one do not have this flag, then this value has no effect.

public static final int SCROLL_FLAG_SCROLL = 0x1;

* When exiting (scrolling off screen) the view will be scrolled until it is

* 'collapsed'. The collapsed height is defined by the view''s minimum height.

* @see ViewCompat#getMinimumHeight(View)

* @see View#setMinimumHeight(int)

public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;

* When entering (scrolling on screen) the view will scroll on any downwards

* scroll event, regardless of whether the scrolling view is also scrolling. This

* is commonly referred to as the 'quick return' pattern.

public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;

* An additional flag for 'enterAlways' which modifies the returning view to

* only initially scroll back to it''s collapsed height. Once the scrolling view has

* reached the end of it''s scroll range, the remainder of this view will be scrolled

* into view. The collapsed height is defined by the view''s minimum height.

* @see ViewCompat#getMinimumHeight(View)

* @see View#setMinimumHeight(int)

public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;

* Upon a scroll ending, if the view is only partially visible then it will be snapped

* and scrolled to it''s closest edge. For example, if the view only has it''s bottom 25%

* displayed, it will be scrolled off screen completely. Conversely, if it''s bottom 75% * is visible then it will be scrolled fully into view.

public static final int SCROLL_FLAG_SNAP = 0x10;

用來使ToolBarLayout滑動時有個緊貼效果

3.以及Recyclerview中增加如下命名空間:

app:layout_behavior="@string/appbar_scrolling_view_behavior"

用來綁定和AppBarLayout的關聯滾動效果;

2.解題思路:

1.前情提要:

android coordinatorlayout滑動卡頓,CoordinatorLayout和AppBarLayout滑動彈跳(回彈)問題解決...

AppBarLayout類結構:.png

android coordinatorlayout滑動卡頓,CoordinatorLayout和AppBarLayout滑動彈跳(回彈)問題解決...

其中Behavior類結構:.png

繼承關系如下:

1.public static class Behavior extends HeaderBehavior

2.abstract class HeaderBehavior extends ViewOffsetBehavior

3.class ViewOffsetBehavior extends CoordinatorLayout.Behavior

2.在AppBarLayout.Behavior中我們發現這個方法用來實作貼緊效果的

private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) {

final int offset = getTopBottomOffsetForScrollingSibling();

final int offsetChildIndex = getChildIndexOnOffset(abl, offset);

if (offsetChildIndex >= 0) {

final View offsetChild = abl.getChildAt(offsetChildIndex);

final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams();

final int flags = lp.getScrollFlags();

if ((flags & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) {

// We're set the snap, so animate the offset to the nearest edge

int snapTop = -offsetChild.getTop();

int snapBottom = -offsetChild.getBottom();

if (offsetChildIndex == abl.getChildCount() - 1) {

// If this is the last child, we need to take the top inset into account

snapBottom += abl.getTopInset();

}

if (checkFlag(flags, LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED)) {

// If the view is set only exit until it is collapsed, we'll abide by that

snapBottom += ViewCompat.getMinimumHeight(offsetChild);

} else if (checkFlag(flags, LayoutParams.FLAG_QUICK_RETURN

| LayoutParams.SCROLL_FLAG_ENTER_ALWAYS)) {

// If it's set to always enter collapsed, it actually has two states. We

// select the state and then snap within the state

final int seam = snapBottom + ViewCompat.getMinimumHeight(offsetChild);

if (offset < seam) {

snapTop = seam;

} else {

snapBottom = seam;

}

}

final int newOffset = offset < (snapBottom + snapTop) / 2

? snapBottom

: snapTop;

animateOffsetTo(coordinatorLayout, abl,

MathUtils.clamp(newOffset, -abl.getTotalScrollRange(), 0), 0);

}

}

}

在兩個地方執行:

1.

@Override

void onFlingFinished(CoordinatorLayout parent, AppBarLayout layout) {

// At the end of a manual fling, check to see if we need to snap to the edge-child

snapToChildIfNeeded(parent, layout);

}

2.

@Override

public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,

View target, int type) {

if (type == ViewCompat.TYPE_TOUCH) {

// If we haven't been flung then let's see if the current view has been set to snap

snapToChildIfNeeded(coordinatorLayout, abl);

}

// Keep a reference to the previous nested scrolling child

mLastNestedScrollingChildRef = new WeakReference<>(target);

}

明顯我們需要對第二種執行情況進行修改,使得正在滑動的時候不執行貼緊操作,實作方式就是繼承Behavior類重寫其onStopNestedScroll方法,判斷目前是否處于慣性滑動操作之中。

3.自定義Behevior源碼如下:

public class WhlBehavior extends AppBarLayout.Behavior {

private boolean isFlinging = false;

public WhlBehavior() {}

public WhlBehavior(Context context, AttributeSet attrs) {

super(context, attrs);

}

@Override

public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {

//如果不是慣性滑動,讓他可以執行緊貼操作

if (!isFlinging) {

super.onStopNestedScroll(coordinatorLayout, abl, target, type);

}

}

@Override

public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {

super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);

//type==1時處于非慣性滑動

if (type == 1) {

isFlinging = false;

}

}

@Override

public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {

//慣性滑動的時候設定為true

isFlinging = true;

return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);

}

}

附上執行效果圖:

android coordinatorlayout滑動卡頓,CoordinatorLayout和AppBarLayout滑動彈跳(回彈)問題解決...

執行效果圖.gif