// In SwipeRefreshLayout This layout should be made the parent of the view that will be refreshed as a result of the gesture and can only support one direct child.
// In SwipeRefreshLayout privatevoidensureTarget(){ // Don't bother getting the parent height if the parent hasn't been laid // out yet. if (mTarget == null) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (!child.equals(mCircleView)) { mTarget = child; break; } } } }
// In SwipeRefreshLayout @Override publicbooleanonInterceptTouchEvent(MotionEvent ev){ ... if (!isEnabled() || mReturningToStart || canChildScrollUp() || mRefreshing || mNestedScrollInProgress) { // Fail fast if we're not in a state where a swipe is possible returnfalse; } ... }
除了一些Boolean的參數,還有一個特殊的函示canChildScrollUp():
// In SwipeRefreshLayout publicbooleancanChildScrollUp(){ if (mChildScrollUpCallback != null) { return mChildScrollUpCallback.canChildScrollUp(this, mTarget); } if (mTarget instanceof ListView) { return ListViewCompat.canScrollList((ListView) mTarget, -1); } return mTarget.canScrollVertically(-1); }
// In SwipeRefreshLayout @Override publicvoidonNestedScrollAccepted(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 startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL); mTotalUnconsumed = 0; mNestedScrollInProgress = true; }
// In SwipeRefreshLayout @Override publicvoidonNestedPreScroll(View target, int dx, int dy, int[] consumed){ // If we are in the middle of consuming, a scroll, then we want to move the spinner back up // before allowing the list to scroll if (dy > 0 && mTotalUnconsumed > 0) { if (dy > mTotalUnconsumed) { consumed[1] = dy - (int) mTotalUnconsumed; mTotalUnconsumed = 0; } else { mTotalUnconsumed -= dy; consumed[1] = dy; } moveSpinner(mTotalUnconsumed); }
// If a client layout is using a custom start position for the circle // view, they mean to hide it again before scrolling the child view // If we get back to mTotalUnconsumed == 0 and there is more to go, hide // the circle so it isn't exposed if its blocking content is moved if (mUsingCustomStart && dy > 0 && mTotalUnconsumed == 0 && Math.abs(dy - consumed[1]) > 0) { mCircleView.setVisibility(View.GONE); } // Now let our nested parent consume the leftovers finalint[] parentConsumed = mParentScrollConsumed; if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) { consumed[0] += parentConsumed[0]; consumed[1] += parentConsumed[1]; } }
// In SwipeRefreshLayout @Override publicvoidonNestedScroll(final View target, finalint dxConsumed, finalint dyConsumed, finalint dxUnconsumed, finalint dyUnconsumed){ // Dispatch up to the nested parent first dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, mParentOffsetInWindow);
// This is a bit of a hack. Nested scrolling works from the bottom up, and as we are // sometimes between two nested scrolling views, we need a way to be able to know when any // nested scrolling parent has stopped handling events. We do that by using the // 'offset in window 'functionality to see if we have been moved from the event. // This is a decent indication of whether we should take over the event stream or not. finalint dy = dyUnconsumed + mParentOffsetInWindow[1]; if (dy < 0 && !canChildScrollUp()) { mTotalUnconsumed += Math.abs(dy); moveSpinner(mTotalUnconsumed); } }
// In SwipeRefreshLayout @Override publicvoidonNestedPreScroll(View target, int dx, int dy, int[] consumed){ // If we are in the middle of consuming, a scroll, then we want to move the spinner back up // before allowing the list to scroll if (dy > 0 && mTotalUnconsumed > 0) { if (dy > mTotalUnconsumed) { consumed[1] = dy - (int) mTotalUnconsumed; mTotalUnconsumed = 0; } else { mTotalUnconsumed -= dy; consumed[1] = dy; } moveSpinner(mTotalUnconsumed); } ... }
// In UpSwipeRefreshLayout privatestaticfinalint OVER_SCROLL_TOTAL_DISTANCE = 200; privatevoidmoveTarget(int offset){ finalint currentTop = mTarget.getTop(); // Hit the max scroll distance if (currentTop + offset < -OVER_SCROLL_TOTAL_DISTANCE) { // The final offset will be more than needed, so take the limit. offset = -OVER_SCROLL_TOTAL_DISTANCE - currentTop; } elseif (currentTop + offset >= 0) { // Scrolling back the original position during dragging the target view offset = -currentTop; mTotalDrag = 0.0f; }