天天看点

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