Error using the RecyclerView: The specified child already has a parent
Asked Answered
H

6

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)
Honeywell answered 27/10, 2014 at 20:39 Comment(5)
Use inflater.inflate(R.layout.my_text_view, parent, false);Refluent
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); insteadAblaze
@Ablaze you are magician. I have tried that before so many times. But I now it just Work. Thanks a lot for quick responseHoneywell
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.Bestride
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.Pleinair
H
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);
Heterotrophic answered 28/10, 2014 at 14:28 Comment(3)
Also you can't use View v = inflater.inflate(R.layout.my_text_view, parent); because it defaults to true.Antipus
In general why the View that we pass to a ViewHolder shouldn't have a parent? How does caching exactly happen here?Claire
because its going to be added to the recyclerview and views can only have one parent at a timeHeterotrophic
R
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;
    }
Rumormonger answered 22/1, 2015 at 10:6 Comment(3)
v.removeView() is not needed! Just pass v itself into ViewHolder constructor instead of view. And call findViewById() inside ViewHolder constructor.Formyl
inflating the view using parent.getContext() solved it for me. thanks!Quatrain
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."Freyah
N
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

Nydia answered 20/2, 2018 at 11:12 Comment(9)
Did you happen to find a solution to this?Staminody
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...Snowblink
I used transition.excludeChildren(myRecycler, true) to fix this.Gustave
@Gustave I'm using TransitionManager.beginDelayedTransition(view). on which object do I invoke excludeChildren()?Court
@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.Gustave
@Gustave Creating a custom Transition fixed the issue for me, thanks.Xray
@Gustave your solution works fixes crash, but animation stops to working.Swimmingly
It has also happened to me. When trying to scroll really fast in my RV, the crash happens @Gustave how to invoke excludeChilder() on a custom Transition class?Sassan
you can check for my answer here to use Transition also support for multiple ids / views when using excludeChildren()Sassan
Y
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

Yeomanry answered 24/3, 2018 at 13:3 Comment(0)
B
1

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

Branen answered 1/8, 2019 at 18:33 Comment(0)
S
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:

Sassan answered 16/12, 2019 at 11:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.