Throw if onCreateViewHolder returns attached view
Bug: 38496391
Bug: 38375597
Test: RecyclerViewBasicTest
RecyclerView doesn't support pre-attached ViewHolders. Previously this
failure would occur in a racey way, depending on how the ViewHolder is
used.
Change-Id: I74b48405fab07e0abb0012726289d772ed93793e
diff --git a/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
index a287979..41b3627 100644
--- a/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
@@ -6643,11 +6643,19 @@
* @see #onCreateViewHolder(ViewGroup, int)
*/
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
- TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
- final VH holder = onCreateViewHolder(parent, viewType);
- holder.mItemViewType = viewType;
- TraceCompat.endSection();
- return holder;
+ try {
+ TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
+ final VH holder = onCreateViewHolder(parent, viewType);
+ if (holder.itemView.getParent() != null) {
+ throw new IllegalStateException("ViewHolder views must not be attached when"
+ + " created. Ensure that you are not passing 'true' to the attachToRoot"
+ + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
+ }
+ holder.mItemViewType = viewType;
+ return holder;
+ } finally {
+ TraceCompat.endSection();
+ }
}
/**
diff --git a/v7/recyclerview/tests/res/layout/item_view.xml b/v7/recyclerview/tests/res/layout/item_view.xml
index ffb2153..3addcfd 100644
--- a/v7/recyclerview/tests/res/layout/item_view.xml
+++ b/v7/recyclerview/tests/res/layout/item_view.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/item_bg"
@@ -22,4 +23,4 @@
android:focusableInTouchMode="true"
android:orientation="vertical">
-</View>
\ No newline at end of file
+</View>
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index 3357c2f..50e2d8b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -31,11 +31,14 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.recyclerview.test.R;
import android.util.AttributeSet;
import android.util.SparseArray;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -66,7 +69,7 @@
}
private Context getContext() {
- return InstrumentationRegistry.getContext();
+ return InstrumentationRegistry.getTargetContext();
}
@Test
@@ -367,6 +370,40 @@
}
@Test
+ public void createAttachedException() {
+ mRecyclerView.setAdapter(new RecyclerView.Adapter() {
+ @NonNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
+ int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_view, parent, true)
+ .findViewById(R.id.item_view); // find child, since parent is returned
+ return new RecyclerView.ViewHolder(view) {};
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+ fail("shouldn't get here, should throw during create");
+ }
+
+ @Override
+ public int getItemCount() {
+ return 1;
+ }
+ });
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+ try {
+ measure();
+ //layout();
+ fail("IllegalStateException expected");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+
+ @Test
public void prefetchChangesCacheSize() {
mRecyclerView.setAdapter(new MockAdapter(20));
MockLayoutManager mlm = new MockLayoutManager() {
@@ -790,4 +827,4 @@
void addItems(int start, int count) {
}
}
-}
\ No newline at end of file
+}