ViewDragHelper实现RecyclerView下拉刷新和上拉加载更多的类似效果

·  阅读 662

需求

  1. 垂直方向的ViewPager+Fragment,ViewPager不能滑动,每个Fragment滑动到顶部或者底部,有类似IOS弹性效果,滑动一段距离可以跳到上一个或下一个Fragment。
  2. 实际效果:见懒人畅听标签列表页效果。

代码

public class PullSlideParent extends ViewGroup {

    private final ViewDragHelper mDragHelper;
    private final ViewGroup mHeader;
    private final ViewGroup mFooter;
    private int mHeaderMeasuredHeight;
    private int mFooterMeasuredHeight;

    private View mDragContentView;
    private float lastY;
    private float deltaY;
    private OnPullListener onPullListener;
    private boolean up = false;  //上拉
    private boolean down = false; //下拉
    private boolean canUp = true;
    private boolean canDown = true;

    private static final float SWIPE_BACK_FACTOR = 0.9f;
    private float swipeBackFraction;

    public PullSlideParent(Context context) {
        this(context, null);
    }

    public PullSlideParent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PullSlideParent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDragHelper = ViewDragHelper.create(this, new PullSlideParent.DragHelperCallback());
        mHeader = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.pull_side_header, this, false);
        mFooter = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.pull_side_footer, this, false);
        addView(mHeader);
        addView(mFooter);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        if (childCount != 3) {
            throw new IllegalStateException("PullSlideParent must contains only three direct child.");
        }

        measureChildren(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (getChildCount() != 3) {
            return;
        }

        mHeaderMeasuredHeight = mHeader.getMeasuredHeight();
        mFooterMeasuredHeight = mFooter.getMeasuredHeight();
        mHeader.layout(0, -mHeaderMeasuredHeight, getMeasuredWidth(), 0);
        mFooter.layout(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() + mFooterMeasuredHeight);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child instanceof RecyclerView) {
                mDragContentView = child;
                mDragContentView.layout(0, 0, mDragContentView.getMeasuredWidth(), mDragContentView.getMeasuredHeight());
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = ev.getY();
                deltaY = 0;
                mDragHelper.shouldInterceptTouchEvent(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                deltaY = ev.getY() - lastY;
                lastY = ev.getY();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mDragHelper.shouldInterceptTouchEvent(ev);
                deltaY = 0;
                break;
            default:
        }
        if (canDown && deltaY > 0 && !mDragContentView.canScrollVertically(-1)) {
            return mDragHelper.shouldInterceptTouchEvent(ev);
        }

        if (canUp && deltaY < 0 && !mDragContentView.canScrollVertically(1)) {
            return mDragHelper.shouldInterceptTouchEvent(ev);
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        if (mDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    public void setCanUp(boolean canUp) {
        this.canUp = canUp;
    }

    public void setCanDown(boolean canDown) {
        this.canDown = canDown;
    }

    public void setOnPullListener(PullSlideParent.OnPullListener onPullListener) {
        this.onPullListener = onPullListener;
    }

    public interface OnPullListener {
        void onPullUp();

        void onPullDown();
    }

    private class DragHelperCallback extends ViewDragHelper.Callback {

        @Override
        public boolean tryCaptureView(@NonNull View child, int pointerId) {
            return child == mDragContentView;
        }

        @Override
        public int getViewVerticalDragRange(@NonNull View child) {
            return Math.max(mHeaderMeasuredHeight, mFooterMeasuredHeight);
        }

        @Override
        public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
            if (top > 0) {
                swipeBackFraction = 1.0f * top / mHeaderMeasuredHeight;
                mHeader.setTranslationY(top);
            } else if (top < 0) {
                swipeBackFraction = -1.0f * top / mFooterMeasuredHeight;
                mFooter.setTranslationY(top);
            } else {
                swipeBackFraction = 0f;
                down = false;
                up = false;
                mHeader.setTranslationY(top);
                mFooter.setTranslationY(top);
            }
        }

        @Override
        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
            if (top > 0) {
                down = true;
                return Math.min(mHeaderMeasuredHeight, top);
            } else if (top < 0) {
                up = true;
                return Math.max(top, -mFooterMeasuredHeight);
            }
            return 0;
        }

        @Override
        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
            if (onPullListener != null && swipeBackFraction > SWIPE_BACK_FACTOR) {
                if (up) {
                    onPullListener.onPullUp();
                } else if (down) {
                    onPullListener.onPullDown();
                }
            }
            mDragHelper.smoothSlideViewTo(releasedChild, 0, 0);
            ViewCompat.postInvalidateOnAnimation(PullSlideParent.this);
        }
    }
}
复制代码
分类:
Android
标签: