Merge "recyclerview: fix getViewForPosition crash in onMeasure()" into oc-support-26.0-dev
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 8033400..33c706f 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -3144,6 +3144,14 @@
}
mAdapterUpdateDuringMeasure = false;
resumeRequestLayout(false);
+ } else if (mState.mRunPredictiveAnimations) {
+ // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
+ // this means there is already an onMeasure() call performed to handle the pending
+ // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
+ // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
+ // because getViewForPosition() will crash when LM uses a child to measure.
+ defaultOnMeasure(widthSpec, heightSpec);
+ return;
}
if (mAdapter != null) {
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 be687c3..1547049 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -252,6 +252,66 @@
}
@Test
+ public void preditiveMeasuredCrashTest() throws Throwable {
+ final RecyclerView rv = new RecyclerView(getActivity());
+ final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true) {
+ @Override
+ public void onAttachedToWindow(RecyclerView view) {
+ super.onAttachedToWindow(view);
+ assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
+ }
+
+ @Override
+ public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
+ super.onDetachedFromWindow(view, recycler);
+ assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
+ }
+
+ @Override
+ public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
+ int widthSpec,
+ int heightSpec) {
+ if (state.getItemCount() > 0) {
+ // A typical LayoutManager will use a child view to measure the size.
+ View v = recycler.getViewForPosition(0);
+ }
+ super.onMeasure(recycler, state, widthSpec, heightSpec);
+ }
+ };
+ lm.setSupportsPredictive(true);
+ lm.setAutoMeasureEnabled(false);
+ rv.setHasFixedSize(false);
+ final TestAdapter adapter = new TestAdapter(0);
+ rv.setAdapter(adapter);
+ rv.setLayoutManager(lm);
+ lm.expectLayouts(1);
+ setRecyclerView(rv);
+ lm.waitForLayout(2);
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ViewGroup parent = (ViewGroup) rv.getParent();
+ parent.removeView(rv);
+ // setting RV as child of LinearLayout using MATCH_PARENT will cause
+ // RV.onMeasure() being called twice before layout(). This may cause crash.
+ LinearLayout linearLayout = new LinearLayout(parent.getContext());
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ parent.addView(linearLayout,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ linearLayout.addView(rv, ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ }
+ });
+
+ lm.expectLayouts(1);
+ adapter.addAndNotify(1);
+ lm.waitForLayout(2);
+ checkForMainThreadException();
+ }
+
+ @Test
public void detachRvAndLayoutManagerProperly() throws Throwable {
final RecyclerView rv = new RecyclerView(getActivity());
final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true) {