用viewDragHelper来写刷新控件<三>

前面第一章和第二章讲述了如何实现一个基本的上拉下拉控件,接下来讨论一下里面关键的几个类的实现以及如何增强功能

  • 工具类实现
  • 没有数据时的空白页面支持

ScrollViewCompat工具类

我们在上拉下拉动作的时候,需要通过ScrollViewCompat工具类来判断是否达到顶端或者底端,一般可滑动的控件分为ScrollView,ListView,WebViewRecyclerViewListViewRecyclerView是通过当前显示的子视图是否第一个或者最后一个以及是否有未显示的视图判断,ScrollView是通过scrollY值判断

/**
 * 该视图控件还能否向下拉动
 *
 * @param mTarget
 * @return true-未到顶部,false-到顶部
 */
public static boolean canSmoothDown(View mTarget) {
    if (android.os.Build.VERSION.SDK_INT < 14) {
        if (mTarget instanceof AbsListView) {
            final AbsListView absListView = (AbsListView) mTarget;
            return absListView.getChildCount() > 0
                    && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
                    .getTop() < absListView.getPaddingTop());
        } else if (mTarget instanceof RecyclerView) {
            final RecyclerView recyclerView = (RecyclerView) mTarget;
            LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager();
            if ((lm.findFirstVisibleItemPosition() == 0)) {
                View firstView = lm.findViewByPosition(0);
                return firstView.getTop() < 0;
            } else {
                return true;
            }
        } else {
            return mTarget.getScrollY() > 0;
        }
    } else {
        return ViewCompat.canScrollVertically(mTarget, -1);
    }
}

/**
 * 该视图控件还能否向上拉动
 *
 * @param mTarget
 * @return true-未到底部,false-到底部
 */
public static boolean canSmoothUp(View mTarget) {
    if (android.os.Build.VERSION.SDK_INT < 14) {
        if (mTarget instanceof AbsListView) {
            final AbsListView absListView = (AbsListView) mTarget;
            View lastChild = absListView.getChildAt(absListView.getChildCount() - 1);
            if (lastChild != null) {
                if (absListView.getFirstVisiblePosition() == 0 && absListView.getLastVisiblePosition() == (absListView.getCount() - 1)) {
                    return false;
                }
                return (absListView.getLastVisiblePosition() < (absListView.getCount() - 1))
                        && lastChild.getBottom() > absListView.getPaddingBottom();
            } else {
                return false;
            }
        } else if (mTarget instanceof RecyclerView) {
            final RecyclerView recyclerView = (RecyclerView) mTarget;
            LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager();
            int count = recyclerView.getAdapter().getItemCount() - 1;
            if (lm.canScrollVertically()) {
                return !(lm.findLastVisibleItemPosition() == count);
            } else {
                return false;
            }
        } else {
            View scrollChild = ((ViewGroup) mTarget).getChildAt(0);
            if (scrollChild == null) {
                return false;
            } else {
                int childHeight = scrollChild.getMeasuredHeight();
                return (mTarget.getScrollY() + mTarget.getHeight()) < childHeight;
            }
        }
    } else {
        return ViewCompat.canScrollVertically(mTarget, 1);
    }
}

空白页面支持

一般在没有数据的时候我们希望显示一个缺省的空白页面,类似于ListViewemptyView

这里除了添加上空白页面支持外,还需做一些小的改变。在显示空白页面时,我们希望只有动画可拽动而空白页面不会被拽动,也就是侵入式的下拉效果(非侵入式效果即之前的正常列表的下拉)

    public DragRefreshLayout(Context context, AttributeSet attrs) {
        // .......
        emptyId = a.getResourceId(R.styleable.refresh_DragRefreshLayout_refresh_empty, 0);
        // .......
    }

    private void ensureTarget() {
        // .....
        if (emptyId != 0) {
            emptyView = findViewById(emptyId);
            emptyView.setClickable(true);
        }
    }

VDH的tryCaptureView中增加是否是emptyView的判断

    @Override
    public boolean tryCaptureView(View child, int pointerId) {
        return child == mTarget
                || (child == emptyView && emptyView.isShown())
                || child == refreshView
                || child == loadView;
    }

不要忘了clampViewPositionVertical

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
        if (child == mTarget || (child == emptyView && emptyView.isShown())) {
            status = ScrollStatus.DRAGGING;
            if (contentTop + dy > DRAG_MAX_RANGE) {
                return DRAG_MAX_RANGE;
            } else if (contentTop + dy < -DRAG_MAX_RANGE) {
                return -DRAG_MAX_RANGE;
            } else {
                return top;
            }
        } else {
            status = ScrollStatus.DRAGGING;
            if (contentTop + dy > DRAG_MAX_RANGE) {
                return DRAG_MAX_RANGE - refreshView.getMeasuredHeight();
            } else if (contentTop + dy < -DRAG_MAX_RANGE) {
                return getMeasuredHeight() - getPaddingBottom() - DRAG_MAX_RANGE;
            } else {
                return top;
            }
        }
    }

侵入式下拉即下拉时emptyView的位置不变,其他视图的位置依然变化:

    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        if (changedView == mTarget) {
            //......
        } else if (changedView == emptyView && emptyView.isShown()) {
            refreshView.offsetTopAndBottom(dy);
            loadView.offsetTopAndBottom(dy);
            contentTop = top;
            invalidate();
        } else {
            // .......
        }
    }

因为控件不维护数据内容,控件本身没有设置空白页面展示与隐藏的能力,不比ListView,因此只能在业务中判断数据是否为空并且显示或者隐藏空白页面

到此,整个DragRefresh控件完成,have a happy day!

你可能感兴趣的:(用viewDragHelper来写刷新控件<三>)