内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

AppBarLayout v26+抖动BugFix

2018-02-13 15:42 出处:清屏网 人气: 评论(0

Android v26.x support library中升级了NestedScrolling API,以此修复了CoordinatorLayout+AppBarLayout的fling卡顿的问题, 戳这里看Chris大佬的文章说明

所以我们可以仅通过布局就能实现以下效果:

期望.gif

一切看起来跟初恋一样美好,但是在实际的应用中却遇到了些问题。

一图胜千言,所以还是看图:

现实.gif

因为业务需求导致AppBarLayout高度很大,这样的情况下很容易触发一个操作:

粉色或绿色部分施加一个向上的fling,紧接着在白色列表部分施加一个向下的fling ,就会出现上图这样的激烈的抖动回弹。

到这里,问题也描述的差不多了,有遇到同样问题&心急的朋友可能反手就是一拖鞋

交出代码.png

Demo在文章底部

public class FixBounceV26Behavior extends AppBarLayout.Behavior {

    private OverScroller mScroller1;

    public FixBounceV26Behavior() {
        super();
    }

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

    /**
     * 反射注入Scroller以获取其引用
     *
     * @param context
     */
    private void bindScrollerValue(Context context) {
        if (mScroller1 != null) return;
        mScroller1 = new OverScroller(context);
        try {
            Class<?> clzHeaderBehavior = getClass().getSuperclass().getSuperclass();
            Field fieldScroller = clzHeaderBehavior.getDeclaredField("mScroller");
            fieldScroller.setAccessible(true);
            fieldScroller.set(this, mScroller1);
        } catch (Exception e) {}
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
        if (type == ViewCompat.TYPE_NON_TOUCH) {
            //fling上滑appbar然后迅速fling下滑list时, HeaderBehavior的mScroller并未停止, 会导致上下来回晃动
            if (mScroller1.computeScrollOffset()) {
                mScroller1.abortAnimation();
            }
            //当target滚动到边界时主动停止target fling,与下一次滑动产生冲突
            if (getTopAndBottomOffset() == 0) {
                ViewCompat.stopNestedScroll(target, type);
            }
        }
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
    }
}

这就完了?

是的。

上面的代码已经能简单地解决问题了,想知道的更详细的话可以往下看看。

我就知道你是喜欢我的.png

  • Round 1

    CoordinatorLayout和子View的联动时通过CoordinatorLayout.Behavior实现的,AppBarLayout使用的Behavior继承了HeaderBehavior<AppBarLayout>。

    问题就在这里。

    HeaderBehavior的onTouchEvent中使用Scroller实现了fling操作,但是没有通过NestedScrolling API对外开放,也就说一旦HeaderBehavior的fling动作形成,无法由外部主动中断。

@Override
    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
       switch (ev.getActionMasked()) {
            //剔除了多余部分
            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);
                } 
        }
        return true;
    }

final boolean fling(CoordinatorLayout coordinatorLayout, V layout, int minOffset,
            int maxOffset, float velocityY) {
        if (mFlingRunnable != null) {
            layout.removeCallbacks(mFlingRunnable);
            mFlingRunnable = null;
        }

        if (mScroller == null) {
            mScroller = new OverScroller(layout.getContext());
        }

        mScroller.fling(
                0, getTopAndBottomOffset(), // curr
                0, Math.round(velocityY), // velocity.
                0, 0, // x
                minOffset, maxOffset); // y

        if (mScroller.computeScrollOffset()) {
            mFlingRunnable = new FlingRunnable(coordinatorLayout, layout);
            ViewCompat.postOnAnimation(layout, mFlingRunnable);
            return true;
        } else {
            onFlingFinished(coordinatorLayout, layout);
            return false;
        }
    }
  • Round 2

    与AppBarLayout同层级的RecyclerView可以通过升级过的NestedScrolling API对AppBarLayout产生影响,比如RecyclerView向下fling时滑动到item 0之后,如果AppBarLayout可以滑动时会给AppBarLayout施加一个同样向下的fling动作,以此形成一个连贯的下滑fling。

    那么问题来了。

    当HeaderBehavior产生的向上的fling没有结束时,RecyclerView又送来向下的fling,抖动就产生了。

    我可是讲道理的人.png

那我们继续了.png

  • Round 3

    既然知道了问题所在,那么我们就来解决问题。

    看一哈HeaderBehavior的代码发现mScroller没有提供对外的调用接口而且使用默认的修饰符,但是有一个好消息就是mScroller是在fling()方法中通过判空来初始化的并且没有重置mScroller的操作。

    所以我们可以通过子类继承之后在构建时就为mScroller注入实例,这样我们就可以获得mScroller的引用了。

    然后在RecyclerView产生fling时主动中断mScroller.fling。

/**
     * 反射注入Scroller以获取其引用
     *
     * @param context
     */
    private void bindScrollerValue(Context context) {
        if (mScroller1 != null) return;
        mScroller1 = new OverScroller(context);
        try {
            //android.support.design.widget.HeaderBehavior
            Class<?> clzHeaderBehavior = getClass().getSuperclass().getSuperclass();
            Field fieldScroller = clzHeaderBehavior.getDeclaredField("mScroller");
            fieldScroller.setAccessible(true);
            fieldScroller.set(this, mScroller1);
        } catch (Exception e) {}
    }
    
    /**
      * 来自RecyclerView的fling会触发NestedScrolling API
      */
    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
        if (type == ViewCompat.TYPE_NON_TOUCH) {
            //fling上滑appbar然后迅速fling下滑list时, HeaderBehavior的mScroller并未停止, 会导致上下来回晃动
            if (mScroller1.computeScrollOffset()) {
                mScroller1.abortAnimation();
            }
            //当target滚动到边界时主动停止target fling,与下一次滑动产生冲突
            if (getTopAndBottomOffset() == 0) {
                ViewCompat.stopNestedScroll(target, type);
            }
        }
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
    }

最后把写好的Behavior应用到AppBarLayout上就OK了。

<android.support.design.widget.AppBarLayout
        android:id="@+id/layout_appbar"
        android:layout_width="match_parent"
        app:layout_behavior="hello.kay.chestnut.fix.FixBounceV26Behavior "
        android:layout_height="wrap_content">

代码很少,实现方式也很简单,主要是分享下解决此类问题的一种思路,希望能对大家有所帮助。

Demo传送阵

分享给小伙伴们:
本文标签: AppBarLayoutBugFix

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号