56

I am trying to create a listView with a new RecyvlerView Adapter. I have followed the exact guide present on android developer resources. But this is giving me a strange error: The specified child already has a parent. You must call removeView() on the child's parent first. I have the latest SDK. I also define dependencies in gradle.

MyActivity (Main Activity):

public class MyActivity extends Activity {

    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(new String[]{"Zain", "Nadeem"});

        mRecyclerView.setAdapter(mAdapter);
    }
}

MyAdapter :

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private String[] mDataset;    
    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // create a new view
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());  

        View v = inflater.inflate(R.layout.my_text_view, parent, true);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element    
        holder.mTextView.setText(mDataset[position]);
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTextView;
        public ViewHolder(View v) {
            super(v);
            mTextView = (TextView) v.findViewById(R.id.item_title);
        }
    }
}

my_text_view.xml (layout for list items):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="80dp"

    >
    <!-- title -->
    <TextView
        android:id="@+id/item_title"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/darker_gray"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:textSize="22dp" />

</RelativeLayout>

activity_my.xml (main activity):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    android:background="@android:color/holo_orange_light"
    >
    <!-- A CardView that contains a TextView -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MyActivity"/>
    </LinearLayout>

And here is the logcat output:

10-28 01:20:30.287  22729-22729/osman.zaingz.com.lollipop E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: osman.zaingz.com.lollipop, PID: 22729
    java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
            at android.view.ViewGroup.addViewInner(ViewGroup.java:3562)
            at android.view.ViewGroup.addView(ViewGroup.java:3415)
            at android.view.ViewGroup.addView(ViewGroup.java:3360)
            at android.support.v7.widget.RecyclerView$4.addView(RecyclerView.java:322)
            at android.support.v7.widget.ChildHelper.addView(ChildHelper.java:79)
            at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:4845)
            at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4803)
            at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4791)
            at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1316)
            at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
            at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:522)
            at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1918)
            at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2155)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
            at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1660)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1436)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:374)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1983)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1740)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:996)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5600)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
            at android.view.Choreographer.doCallbacks(Choreographer.java:574)
            at android.view.Choreographer.doFrame(Choreographer.java:544)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Zain Zafar
  • 1,607
  • 4
  • 14
  • 23
  • 9
    Use inflater.inflate(R.layout.my_text_view, parent, false); – Nikola Despotoski Oct 27 '14 at 20:41
  • 4
    the line View v = inflater.inflate(R.layout.my_text_view, parent, true); is attaching the view to the parent, use View v = inflater.inflate(R.layout.my_text_view, parent, false); instead – panini Oct 27 '14 at 20:41
  • @panini you are magician. I have tried that before so many times. But I now it just Work. Thanks a lot for quick response – Zain Zafar Oct 27 '14 at 20:46
  • 4
    I am also having the same code and using View v = inflater.inflate(R.layout.my_text_view, parent, false), but still getting the same exception. – Azmat Apr 17 '15 at 07:52
  • In my case I got the error because the view I gave to ViewHolder was not the 'root' of my entry created by above mentioned inflate(...), but was the inner TextView of my entry. – arberg Oct 26 '15 at 10:30

6 Answers6

115

when inflating you shouldn't attach the view to its parent. you wrote:

View v = inflater.inflate(R.layout.my_text_view, parent, true);

which should be :

View v = inflater.inflate(R.layout.my_text_view, parent, false);
EC84B4
  • 7,676
  • 4
  • 23
  • 34
  • 15
    Also you can't use View v = inflater.inflate(R.layout.my_text_view, parent); because it defaults to true. – Marcio Granzotto Feb 22 '16 at 13:44
  • In general why the View that we pass to a ViewHolder shouldn't have a parent? How does caching exactly happen here? – router Jun 17 '17 at 13:00
  • because its going to be added to the recyclerview and views can only have one parent at a time – EC84B4 Jun 19 '17 at 04:43
17
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="80dp">

<TextView
    android:id="@+id/item_title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="@android:color/darker_gray"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginTop="8dp"
    android:textSize="22dp" />

</RelativeLayout>

RelativeLayout this is parent of your TextView with id item_title. Then when RecyclerView is trying to add TextView that has parent it throw exception.

Try this code:

public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        Log.d(TAG, "onCreateViewHolder");
        RelativeLayout v = (RelativeLayout)LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item, parent, false);
        // set the view's size, margins, paddings and layout parameters
        View view = v.findViewById(R.id.text1);
        v.removeView(view);
        ViewHolder vh = new ViewHolder(view);
        return vh;
    }
parakmiakos
  • 2,994
  • 9
  • 29
  • 43
Magadan Artem
  • 179
  • 1
  • 5
  • 23
    **v.removeView()** is not needed! Just pass **v** itself into ViewHolder constructor instead of **view**. And call *findViewById()* inside ViewHolder constructor. – goRGon Jul 29 '15 at 00:25
  • inflating the view using `parent.getContext()` solved it for me. thanks! – Eric Mar 13 '17 at 07:42
  • Good explanation: "RelativeLayout this is parent of your TextView with id item_title. Then when RecyclerView is trying to add TextView that has parent it throw exception." – Khawar Jun 08 '17 at 08:25
16

It happens too if you scroll really fast your recyclerview. My case was inserting/moving/... items with DiffUtils and calling TransitionManager.beginDelayedTransition(view) on the root layout (and so, the recyclerview) somewhere in my code before the end of recyclerview animations

Victor
  • 197
  • 1
  • 4
  • Did you happen to find a solution to this? – Aashir Jul 13 '18 at 20:50
  • 1
    Wow, this is what fixed my particular issue. Great find, friend! How did you know to check this? If I was on my own, it would have taken me forever... – hellaandrew Aug 11 '18 at 22:05
  • 4
    I used `transition.excludeChildren(myRecycler, true)` to fix this. – Westy92 Aug 28 '18 at 16:31
  • 1
    @Westy92 I'm using `TransitionManager.beginDelayedTransition(view)`. on which object do I invoke `excludeChildren()`? – Hudi Ilfeld Aug 29 '18 at 17:15
  • 1
    @HudiIlfeld `TransitionManager.beginDelayedTransition()` takes a second (optional) parameter, which is a custom `Transition`. Call `excludeChildren` on a custom `Transition` that you use. The default `Transition` type is `AutoTransition`. – Westy92 Aug 29 '18 at 18:49
  • @Westy92 Creating a custom Transition fixed the issue for me, thanks. – Paul T. Sep 21 '18 at 22:12
  • @Westy92 your solution works fixes crash, but animation stops to working. – Alex_297 Oct 25 '18 at 13:40
  • It has also happened to me. When trying to scroll really fast in my RV, the crash happens @Westy92 how to invoke `excludeChilder()` on a custom `Transition` class? – mochadwi Dec 16 '19 at 10:53
  • you can check for my [answer here](https://stackoverflow.com/a/59355664/3763032) to use `Transition` also support for multiple ids / views when using `excludeChildren()` – mochadwi Jan 07 '20 at 10:00
1

Maybe it's a little bit late, but I had the same problem (recyclerview-v7:26.1.0) and the reason was that I tried to scroll (by scrollToPosition() method) the list in a position that was currently visible. After I added a check of position before scrolling (scroll only in the invisible position) the issue was resolved

Denis
  • 71
  • 7
1

I solved it by removing `TransitionManager.beginDelayedTransition();

Shivam Yadav
  • 958
  • 11
  • 23
0

In our case, the problems shown when trying to do a custom Transitions & excludeChildren method.

Here's our approach so far to silence the issue

In Kotlin: TransitionHelpers.kt

// single child
private fun useAutoTransition(childView: View, 
                               excludeChildView: Boolean = true
) = AutoTransition().apply {
    excludeChildren(childView, excludeChildView)
    // apply any other method you need here
}

private fun useAutoTransition(@IdRes childViewId: Int, 
                               excludeChildView: Boolean = true
) = AutoTransition().apply {
    excludeChildren(childViewId, excludeChildView)
    // apply any other method you need here
}

// multiple child
fun useAutoTransitions(excludeChildView: Boolean, @IdRes vararg childViewIds: Int) {
        val auto = AutoTransition();

        for (id in childViewIds) {
           auto.excludeChildren(id, excludeChildView)
        }

        return auto;
    }

fun useAutoTransitions(excludeChildView: Boolean, vararg childViews: View) {
        val auto = AutoTransition();

        for (view in childViews) {
           auto.excludeChildren(view, excludeChildView)
        }

        return auto;
    }

In Java: TransitionHelpers.java

I'm using android-retrostream to support Android API below 24, and still using Java 8 lambda & java.util.* -> java9.util.*

Or you can use a Core Library Desugaring, it'll add additional build times though, also there's a problem with Dynamic Features here

import java9.util.stream.Stream;

    public static AutoTransition useAutoTransition(View childView, boolean excludeChildView) {
        AutoTransition auto = new AutoTransition();
        auto.excludeChildren(childView, excludeChildView);

        return auto;
    }

    public static AutoTransition useAutoTransition(@IdRes int childViewId, boolean excludeChildView) {
        AutoTransition auto = new AutoTransition();
        auto.excludeChildren(childViewId, excludeChildView);

        return auto;
    }

    public static AutoTransition useAutoTransitions(final boolean excludeChildView, @IdRes Integer... childViewIds) {
        final AutoTransition auto = new AutoTransition();

        Stream<Integer> ids = Stream.of(childViewIds);
        ids.forEach(id -> auto.excludeChildren(id, excludeChildView));

        return auto;
    }

    public static AutoTransition useAutoTransitions(final boolean excludeChildView, View... childViews) {
        final AutoTransition auto = new AutoTransition();

        Stream<View> views = Stream.of(childViews);
        views.forEach(view -> auto.excludeChildren(view, excludeChildView));

        return auto;
    }

ThatFragment.kt

// I'm using KTX Syntethic here
import kotlinx.android.synthetic.main.activity_transitions.layoutParent
import kotlinx.android.synthetic.main.activity_transitions.recyclerViewChild

// Let's implemented it on TransitionManager
override protected fun onCreate(savedInstanceState: Bundle) {
  // single RecyclerView child using View class
  TransitionManager.beginDelayedTransition(layoutParent, useAutoTransition(recyclerViewChild, true))

  // single RecyclerView child using @IdRes
  TransitionManager.beginDelayedTransition(layoutParent, useAutoTransition(R.id.rv_1, true))

  // multiple RecyclerView child                         
  TransitionManager.beginDelayedTransition(layoutParent, useAutoTransitions(true, R.id.rv_child_1, R.id.rv_child_2, R.id.rv_child_n))
}

Hopefully, it's easy to understand!

Thanks for:

References:

mochadwi
  • 1,190
  • 9
  • 32
  • 87