图解系列之SwipeRefreshLayout原理(三)
目录
- 更多分享:www.catbro.cn
- 我们在上一篇分析了SwipeRefreshlayout中传统事件拦截实现下拉刷新的代码。
- 本篇将分析其通过嵌套滑动实现下拉刷新的代码。
- 在前面的嵌套滑动相关文章中,我们也分析了NestedScrollingParent 和NestedScrollingChild的使用流程,这里就不再细说,有需要的小伙伴可以前往搜索NestedScrollingParent 和NestedScrollingChild。
解码开始
- ok,看到这里就默认你已经了解了NestedScrollingChild和NestedScrollingParent的使用
- 为了便于描述,假设RV为实现了NestedScrollingChild接口的View,如RecyclerView,SRL为SwipeRefreshLayout的简称
图形:
- 交互图如下:
代码块(对图形描述的补充):
- 代码块1:
|
|
-
代码块2: public void onNestedScrollAccepted(View child, View target, int axes) { // Reset the counter of how much leftover scroll needs to be consumed. mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); // Dispatch up to the nested parent
//通知SRL的父类,如CoordinatorLayout startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL); //做一些字段的初始化 mTotalUnconsumed = 0; //记录SRL一共使用了多少y值得量 mNestedScrollInProgress = true; //标记嵌套滑动开始此时传统触摸事件模式失效 }
-
代码块3:
@Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { //执行该代码的情况是你往下拖拽然后网上拖拽,此时需要优先于RV的滚动以便恢复刷新View的状态 //dy大于0为向上滑动产生,而mTotalUnconsumed大于0说明SRL已经有消耗过y值了 if (dy > 0 && mTotalUnconsumed > 0) { if (dy > mTotalUnconsumed) { consumed[1] = dy - (int) mTotalUnconsumed; mTotalUnconsumed = 0; } else { mTotalUnconsumed -= dy; consumed[1] = dy; } //将刷新view移动回mTotalUnconsumed只对应的位置,正值view向下移动,负值向上移动 moveSpinner(mTotalUnconsumed); } //自定义View的情况,可不理会 if (mUsingCustomStart && dy > 0 && mTotalUnconsumed == 0 && Math.abs(dy - consumed[1]) > 0) { mCircleView.setVisibility(View.GONE); } // Now let our nested parent consume the leftovers //同样,将嵌套滑动向上传递 final int[] parentConsumed = mParentScrollConsumed; if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) { consumed[0] += parentConsumed[0]; consumed[1] += parentConsumed[1]; } }
-
代码块4:
@Override public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed, final int dxUnconsumed, final int dyUnconsumed) { // Dispatch up to the nested parent first //同样,消费嵌套滑动数据前先向上传递,让parent先消费 dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, mParentOffsetInWindow); //当parent有消费时,SRL相对于Parent的位置可能会改变,所以要结合mParentOffsetInWindow[1]的值来做处理 final int dy = dyUnconsumed + mParentOffsetInWindow[1]; //dy<0为从上向下滑动 if (dy < 0 && !canChildScrollUp()) { //如果canChildScrollUp()为false,及RV已经滑到顶部了,不能再下拉了,此时自然是SRL的刷新View要出现了 mTotalUnconsumed += Math.abs(dy); //调用moveSpinner来控制SRL刷新View的移动 moveSpinner(mTotalUnconsumed); } }
-
代码块5:
@Override public void onStopNestedScroll(View target) { mNestedScrollingParentHelper.onStopNestedScroll(target); //重置变量为false mNestedScrollInProgress = false; // Finish the spinner for nested scrolling if we ever consumed any // unconsumed nested scroll if (mTotalUnconsumed > 0) { //类似于手势操作up的处理,结束时判断刷新View此时的状态是应该进入刷新状态还是回复到默认位置,代码看下面 finishSpinner(mTotalUnconsumed); mTotalUnconsumed = 0; } // Dispatch up our nested parent stopNestedScroll(); }
-
finishSpinner 方法源码
private void finishSpinner(float overscrollTop) { if (overscrollTop > mTotalDragDistance) { setRefreshing(true, true /* notify */); } else { // cancel refresh mRefreshing = false; mProgress.setStartEndTrim(0f, 0f); Animation.AnimationListener listener = null; if (!mScale) { listener = new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (!mScale) { startScaleDownAnimation(null); } } @Override public void onAnimationRepeat(Animation animation) { } }; } animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener); mProgress.setArrowEnabled(false); } }
总结
- 本次我们就完成了SRL嵌套滑动方面的分析,相信此时的你对嵌套滑动已经并不陌生了
- 后面将会自己从0到1来实现一个刷新控件