美文网首页Android
通过NestedScrolling实现RecyclerView拖

通过NestedScrolling实现RecyclerView拖

作者: x小明 | 来源:发表于2017-06-13 10:57 被阅读0次

    简介:

    • Android5.0后Google为Android的滑动机制提供了NestedScrolling特性,可以使我们对嵌套View更简单事件拦截处理。本篇就对NestedScrolling实际应用做一些讲解。

    </br>

    NestedScrolling与传统事件分发机制作对比:
    • 比如某个外部的ViewGroup拦截掉内部View的事件,那本次事件会被ViewGroup消费并且不会向下传递,如果子view也想处理只能等待下一次手指再按下。
    • NestedScrolling可以很好的解决传统事件拦截的缺点,内部View在滚动的时候通过NestedScrollingChild将dx,dy交给NestedScrollingParent,NestedScrollingParent可对其进行部分消耗,剩余的部分还给内部View。

    而NestedScrolling也是support.v4提供的支持类,在老版本也可以很好的兼容。

    </br>

    描述:

    通过NestedScrolling可以实现哪些效果?
    • 菜单悬停效果
    • 下拉刷新、上拉加载更多
    • 拖拽回弹
    实现嵌套分发主要通过以下两个接口:

    NestedScrollingParent
    NestedScrollingChild

    要使用 NestedScrolling机制,父View 需要实现NestedScrollingParent接口,而子View需要实现NestedScrollingChild接口。RecyclerView已经默认实现了NestedScrollingChild,如果RecyclerView不满足你的业务需求,那需要去实现NestedScrollingChild,这里我只对NestedScrollingParent实现。

    </br>

    实现:

    public class DZStickyNavLayouts extends LinearLayout implements NestedScrollingParent {
    
        private View mHeaderView;
        private View mFooterView;
        private static final int MAX_WIDTH = 500;
        private View mChildView;
        // 解决多点触控问题
        private boolean isRunAnim;
        private int mDrag = 2;
    
        public DZStickyNavLayouts(Context context, AttributeSet attrs) {
            super(context, attrs);
            setOrientation(LinearLayout.HORIZONTAL);
            mHeaderView = new View(context);
            mHeaderView.setBackgroundColor(0xffFFC125);
            mFooterView = new View(context);
            mFooterView.setBackgroundColor(0xffFF3E96);
        }
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            mChildView = getChildAt(0);
            LayoutParams layoutParams = new LayoutParams(MAX_WIDTH, LayoutParams.MATCH_PARENT);
            addView(mHeaderView, 0, layoutParams);
            addView(mFooterView, getChildCount(), layoutParams);
            // 左移
            scrollBy(MAX_WIDTH, 0);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            ViewGroup.LayoutParams params = mChildView.getLayoutParams();
            params.width = getMeasuredWidth();
        }
    
        /**
         * 必须要复写 onStartNestedScroll后调用
         */
        @Override
        public void onNestedScrollAccepted(View child, View target, int axes) {
    
        }
    
        /**
         * 返回true代表处理本次事件
         */
        @Override
        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
            if (target instanceof RecyclerView && !isRunAnim) {
                return true;
            }
            return false;
        }
    
        /**
         * 复位初始位置
         */
        @Override
        public void onStopNestedScroll(View target) {
            startAnimation(new ProgressAnimation());
        }
    
        /**
         * 回弹动画
         */
        private class ProgressAnimation extends Animation {
            // 预留
            private float startProgress = 0;
            private float endProgress = 1;
    
            private ProgressAnimation(){
                isRunAnim = true;
            }
    
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                float progress = ((endProgress - startProgress) * interpolatedTime) + startProgress;
                scrollBy((int) ((MAX_WIDTH - getScrollX()) * progress), 0);
                if (progress == 1) {
                    isRunAnim = false;
                }
            }
    
            @Override
            public void initialize(int width, int height, int parentWidth, int parentHeight) {
                super.initialize(width, height, parentWidth, parentHeight);
                setDuration(260);
                setInterpolator(new AccelerateInterpolator());
            }
        }
    
        @Override
        public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
    
        }
    
        @Override
        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
            // 如果在自定义ViewGroup之上还有父View交给我来处理
            getParent().requestDisallowInterceptTouchEvent(true);
            // dx>0 往左滑动 dx<0往右滑动
            boolean hiddenLeft = dx > 0 && getScrollX() < MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, -1);
            boolean showLeft = dx < 0 && !ViewCompat.canScrollHorizontally(target, -1);
            boolean hiddenRight = dx < 0 && getScrollX() > MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, 1);
            boolean showRight = dx > 0 && !ViewCompat.canScrollHorizontally(target, 1);
            if (hiddenLeft || showLeft || hiddenRight || showRight) {
                scrollBy(dx / mDrag, 0);
                consumed[0] = dx;
            }
    
            // 限制错位问题
            if (dx > 0 && getScrollX() > MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, -1)) {
                scrollTo(MAX_WIDTH, 0);
            }
            if (dx < 0 && getScrollX() < MAX_WIDTH && !ViewCompat.canScrollHorizontally(target, 1)) {
                scrollTo(MAX_WIDTH, 0);
            }
        }
    
        @Override
        public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
            return false;
        }
    
    
        @Override
        public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
            // 当RecyclerView在界面之内交给它自己惯性滑动
            if (getScrollX() == MAX_WIDTH) {
                return false;
            }
            return true;
        }
    
        @Override
        public int getNestedScrollAxes() {
            return 0;
        }
    
        /**
         * 限制滑动 移动x轴不能超出最大范围
         */
        @Override
        public void scrollTo(int x, int y) {
            if (x < 0) {
                x = 0;
            } else if (x > MAX_WIDTH * 2) {
                x = MAX_WIDTH * 2;
            }
            super.scrollTo(x, y);
        }
    }
    

    具体实现思路就是给RecyclerView添加头和尾,并控制滑动距离然后通过NestedScrolling进行分发,最后在手指离开界面开启还原动画。

    文章参考:
    http://blog.csdn.net/lmj623565791/article/details/52204039

    相关文章

      网友评论

        本文标题:通过NestedScrolling实现RecyclerView拖

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