CoordinatorLayout+AppBarLayout+R

作者: 小于先森 | 来源:发表于2018-04-28 16:46 被阅读244次

    前言

    项目示例图

    这里写图片描述
    出现的问题链接:https://ask.csdn.net/questions/363070
    跟这个哥们遇到的问题一样一样的;

    原因

    用到的布局结构就是CoordinatorLayout+AppBarLayout+ViewPager 然后Viewpager里是两个RecyclerView;

    当你appbar高度低的时候一般不会触发这个问题,因为appbar fling 豪无用武之地。
    只有你的appbar的高度到达一定的程度,那么问题就出来。

    So You know !! 原因是什么了吧。其实就是appbar 有向下滑动的事件 ,然后你recylerView有向上滑动的事件,两个事件冲突,你往下挪动一下,我往上挪动一下,沙卡拉卡一会。

    解决

    问题出来首先要找根源,recyclerview 跟appbar 关联的桥梁是什么, Behavior ,具体说是 AppBarLayout.Behavior

    但是这个类中并么有处理滑动的东东,so 向上刨根问题。 看他的父类HeaderBehavior

     @Override
        public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
            if (mTouchSlop < 0) {
                mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();
            }
    
            switch (ev.getActionMasked()) {
                case MotionEvent.ACTION_DOWN: {
                    final int x = (int) ev.getX();
                    final int y = (int) ev.getY();
    
                    if (parent.isPointInChildBounds(child, x, y) && canDragView(child)) {
                        mLastMotionY = y;
                        mActivePointerId = ev.getPointerId(0);
                        ensureVelocityTracker();
                    } else {
                        return false;
                    }
                    break;
                }
    
                case MotionEvent.ACTION_MOVE: {
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    if (activePointerIndex == -1) {
                        return false;
                    }
    
                    final int y = (int) ev.getY(activePointerIndex);
                    int dy = mLastMotionY - y;
    
                    if (!mIsBeingDragged && Math.abs(dy) > mTouchSlop) {
                        mIsBeingDragged = true;
                        if (dy > 0) {
                            dy -= mTouchSlop;
                        } else {
                            dy += mTouchSlop;
                        }
                    }
    
                    if (mIsBeingDragged) {
                        mLastMotionY = y;
                        // We're being dragged so scroll the ABL
                        scroll(parent, child, dy, getMaxDragOffset(child), 0);
                    }
                    break;
                }
    
                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);
                    }
                    // $FALLTHROUGH
                case MotionEvent.ACTION_CANCEL: {
                    mIsBeingDragged = false;
                    mActivePointerId = INVALID_POINTER;
                    if (mVelocityTracker != null) {
                        mVelocityTracker.recycle();
                        mVelocityTracker = null;
                    }
                    break;
                }
            }
    
            if (mVelocityTracker != null) {
                mVelocityTracker.addMovement(ev);
            }
    
            return true;
        }
    

    看到了吧 在这个类中赋予了滑动属性。 fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel); 这个就是问题出现的原因, fling 还在进行 你就开始向上滑动,肯定有问题啊。

    解决办法跟NestedScrolling机制 有关。

    不懂这套机制的可以参考这篇文章:https://blog.csdn.net/lmj623565791/article/details/52204039

    简单说呢 就是recyclerView滑动前会触发onNestedPreScroll 方法 告诉他爸爸 让他爸爸知道他要开始滑动了,so我们在这个里做操作直接结束Appbar的滑动就行。

    代码

    import android.content.Context;
    import android.support.design.widget.AppBarLayout;
    import android.support.design.widget.CoordinatorLayout;
    import android.support.v4.view.ViewCompat;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.OverScroller;
    
    import java.lang.reflect.Field;
    
    /**
     * Created by 于德海 on 2018/4/27.
     * package inter.baisong.widgets
     * email : yudehai0204@163.com
     *
     * @describe  自定义behavior  以解决滑动抖动
     *
     */
    
    public class CustomBehavior extends  AppBarLayout.Behavior {
        private OverScroller mScroller;
        public CustomBehavior() {
        }
    
        public CustomBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
            getParentScroller(context);
        }
    
        /**
         * 反射获得滑动属性。
         *
         * @param context
         */
        private void getParentScroller(Context context) {
            if (mScroller != null) return;
            mScroller = new OverScroller(context);
            try {
                Class<?> reflex_class = getClass().getSuperclass().getSuperclass();//父类AppBarLayout.Behavior  父类的父类   HeaderBehavior
                Field fieldScroller = reflex_class.getDeclaredField("mScroller");
                fieldScroller.setAccessible(true);
                fieldScroller.set(this, mScroller);
            } catch (Exception e) {}
        }
        //fling上滑appbar然后迅速fling下滑recycler时, HeaderBehavior的mScroller并未停止, 会导致上下来回晃动
        @Override
        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
            if(mScroller!=null){ //当recyclerView 做好滑动准备的时候 直接干掉Appbar的滑动
                if (mScroller.computeScrollOffset()) {
                    mScroller.abortAnimation();
                }
            }
            if (type == ViewCompat.TYPE_NON_TOUCH&&getTopAndBottomOffset() == 0) { //recyclerview 鸡儿的 惯性比较大 会顶在头部一会儿  到头直接干掉它的滑动
                    ViewCompat.stopNestedScroll(target, type);
            }
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
        }
    
        @Override
        public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent e) {
    
            switch (e.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    break;
            }
    
    
    
            return super.onTouchEvent(parent,child,e);
        }
    }
    
    

    使用位置是在布局文件的appbarlayout中加behavior

    示例:

    <android.support.design.widget.AppBarLayout
                android:id="@+id/mAppbar"
                app:elevation="0dip"
                app:layout_behavior="inter.****.widgets.CustomBehavior"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    

    结尾

    大工告成, game over !

    相关文章

      网友评论

      本文标题:CoordinatorLayout+AppBarLayout+R

      本文链接:https://www.haomeiwen.com/subject/fxoglftx.html