Merge "RecyclerView: add APIs to query Scroller progress" into oc-support-26.0-dev
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index e173069..657fba6 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -12508,6 +12508,8 @@
method public boolean didStructureChange();
method public <T> T get(int);
method public int getItemCount();
+ method public int getRemainingScrollHorizontal();
+ method public int getRemainingScrollVertical();
method public int getTargetScrollPosition();
method public boolean hasTargetScrollPosition();
method public boolean isMeasuring();
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index cb57d08..8d2d7bd 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -1738,6 +1738,7 @@
eatRequestLayout();
onEnterLayoutOrScroll();
TraceCompat.beginSection(TRACE_SCROLL_TAG);
+ fillRemainingScrollValues(mState);
if (x != 0) {
consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
unconsumedX = x - consumedX;
@@ -3548,6 +3549,17 @@
return lastKnownId;
}
+ final void fillRemainingScrollValues(State state) {
+ if (getScrollState() == SCROLL_STATE_SETTLING) {
+ final OverScroller scroller = mViewFlinger.mScroller;
+ state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
+ state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
+ } else {
+ state.mRemainingScrollHorizontal = 0;
+ state.mRemainingScrollVertical = 0;
+ }
+ }
+
/**
* The first step of a layout where we;
* - process adapter updates
@@ -3557,6 +3569,7 @@
*/
private void dispatchLayoutStep1() {
mState.assertLayoutStep(State.STEP_START);
+ fillRemainingScrollValues(mState);
mState.mIsMeasuring = false;
eatRequestLayout();
mViewInfoStore.clear();
@@ -4793,6 +4806,7 @@
eatRequestLayout();
onEnterLayoutOrScroll();
TraceCompat.beginSection(TRACE_SCROLL_TAG);
+ fillRemainingScrollValues(mState);
if (dx != 0) {
hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
overscrollX = dx - hresult;
@@ -11584,6 +11598,7 @@
}
};
}
+
/**
* <p>Contains useful information about the current RecyclerView state like target scroll
* position or view focus. State object can also keep arbitrary data, identified by resource
@@ -11673,6 +11688,9 @@
// that one instead
int mFocusedSubChildId;
+ int mRemainingScrollHorizontal;
+ int mRemainingScrollVertical;
+
////////////////////////////////////////////////////////////////////////////////////////////
State reset() {
@@ -11850,6 +11868,28 @@
: mItemCount;
}
+ /**
+ * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
+ * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
+ * other than {@link #SCROLL_STATE_SETTLING}.
+ *
+ * @return Remaining horizontal scroll distance
+ */
+ public int getRemainingScrollHorizontal() {
+ return mRemainingScrollHorizontal;
+ }
+
+ /**
+ * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
+ * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
+ * other than {@link #SCROLL_STATE_SETTLING}.
+ *
+ * @return Remaining vertical scroll distance
+ */
+ public int getRemainingScrollVertical() {
+ return mRemainingScrollVertical;
+ }
+
@Override
public String toString() {
return "State{"
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
index 38b5345..be687c3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -4571,4 +4571,74 @@
}
}
+ @Test
+ public void testRemainingScrollInLayout() throws Throwable {
+ final RecyclerView recyclerView = new RecyclerView(getActivity());
+ final TestAdapter adapter = new TestAdapter(100);
+
+ final CountDownLatch firstScrollDone = new CountDownLatch(1);
+ final CountDownLatch scrollFinished = new CountDownLatch(1);
+ final int[] totalScrollDistance = new int[] {0};
+ recyclerView.setLayoutManager(new TestLayoutManager() {
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ if (firstScrollDone.getCount() < 1 && scrollFinished.getCount() == 1) {
+ try {
+ assertTrue("layout pass has remaining scroll",
+ state.getRemainingScrollVertical() != 0);
+ assertEquals("layout pass has remaining scroll",
+ 1000 - totalScrollDistance[0], state.getRemainingScrollVertical());
+ } catch (Throwable throwable) {
+ postExceptionToInstrumentation(throwable);
+ }
+ }
+ super.onLayoutChildren(recycler, state);
+ }
+
+ @Override
+ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ firstScrollDone.countDown();
+ totalScrollDistance[0] += dy;
+ if (state.getRemainingScrollVertical() == 0) {
+ // the last scroll pass will have remaining 0
+ scrollFinished.countDown();
+ }
+ return super.scrollVerticallyBy(dy, recycler, state);
+ }
+
+ @Override
+ public boolean canScrollVertically() {
+ return true;
+ }
+ });
+ recyclerView.setAdapter(adapter);
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ setRecyclerView(recyclerView);
+ recyclerView.smoothScrollBy(0, 1000);
+ } catch (Throwable throwable) {
+ postExceptionToInstrumentation(throwable);
+ }
+ }
+ });
+
+ firstScrollDone.await(1, TimeUnit.SECONDS);
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ recyclerView.requestLayout();
+ } catch (Throwable throwable) {
+ postExceptionToInstrumentation(throwable);
+ }
+ }
+ });
+ waitForIdleScroll(recyclerView);
+ assertTrue(scrollFinished.getCount() < 1);
+ assertEquals(totalScrollDistance[0], 1000);
+ }
+
}