234

I have to switch between two layouts frequently. The error is happening in the layout posted below.

When my layout is called the first time, there doesn't occur any error and everything's fine. When I then call a different layout (a blank one) and afterwards call my layout a second time, it throws the following error:

> FATAL EXCEPTION: main
>     java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

My layout-code looks like this:

    tv = new TextView(getApplicationContext()); // are initialized somewhere else
    et = new EditText(getApplicationContext()); // in the code


private void ConsoleWindow(){
        runOnUiThread(new Runnable(){

     @Override
     public void run(){

        // MY LAYOUT:
        setContentView(R.layout.activity_console);
        // LINEAR LAYOUT
        LinearLayout layout=new LinearLayout(getApplicationContext());
        layout.setOrientation(LinearLayout.VERTICAL);
        setContentView(layout);

        // TEXTVIEW
        layout.addView(tv); //  <==========  ERROR IN THIS LINE DURING 2ND RUN
        // EDITTEXT
        et.setHint("Enter Command");
        layout.addView(et);
        }
    }
}

I know this question has been asked before, but it didn't help in my case.

blackst0ne
  • 3,134
  • 4
  • 16
  • 28
  • 2
    Just for someone who gets same error: Make sure you add correct element. Lets say you have to add `LinearLayout` but you add `TextView`. So fix it. – NoWar Mar 19 '18 at 06:53
  • 1
    when using android databinding shouldn't declare view with id 'root', it cause the same error. – Mohammad Reza Khahani Nov 02 '19 at 13:00
  • for those using `TranstitionManager.beginDelayedTransition`, please check my [answer here](https://stackoverflow.com/a/59355664/3763032) – mochadwi Jan 07 '20 at 10:03

31 Answers31

448

The error message says what You should do.

// TEXTVIEW
if(tv.getParent() != null) {
    ((ViewGroup)tv.getParent()).removeView(tv); // <- fix
}
layout.addView(tv); //  <==========  ERROR IN THIS LINE DURING 2ND RUN
// EDITTEXT
Johnny Five
  • 987
  • 1
  • 14
  • 29
Zielony
  • 16,239
  • 6
  • 34
  • 39
103

simply pass the argument

attachtoroot = false

View view = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, false);
Akshay Sahai
  • 2,121
  • 1
  • 17
  • 20
  • 2
    For me, this was a bullseye. Constraint layout for items in a recycleView, dynamically changing constraints based on runtime. ConstraintSet.apply did not work, until this one. So, propagate parent in onCreateViewHolder, but send false as aditional param to inflating. – miroslavign Mar 14 '18 at 09:56
  • 3
    This is the right solution! As @Sniper said, passing null to the second param can cause the child view not to align itself as it should to its parent. OTOH passing the parent can cause the the error in question. So, `attachToRoot` = false, is the the way to go. – Vassilis May 31 '19 at 07:51
  • I have done like this under my `onCreate` => `layout2 = inflater.inflate(R.layout.product_layout, mLinearLayout, false);` and `addProduct.setOnClickListener(v -> { addLayout(); });` next the addLayout => `mLinearLayout.addView(layout2);` – Moeez Aug 02 '22 at 10:09
  • for the 1st time when I click on the button, it does show me the layout. but when I click on the second time I got `The specified child already has a parent. You must call removeView() on the child's parent first.` – Moeez Aug 02 '22 at 10:12
57

I came here on searching the error with my recyclerview but the solution didn't work (obviously). I have written the cause and the solution for it in case of recyclerview. Hope it helps someone.

The error is caused if in the onCreateViewHolder() the following method is followed:

layoutInflater = LayoutInflater.from(context);
return new VH(layoutInflater.inflate(R.layout.single_row, parent));

Instead it should be

return new VH(layoutInflater.inflate(R.layout.single_row, null));
suku
  • 10,507
  • 16
  • 75
  • 120
  • 19
    Sometimes random answers which are not exactly the answers to asked questions help someone else. This worked for me. Thanks! – Ajith M A Aug 02 '17 at 09:56
  • 1
    Nice!, and that's why people should mostly of times pass "null" as 2d parameters in inflaters. Thanks. – superUser Oct 01 '17 at 18:11
  • 5
    Passing Null to the parameter responsible for the parent is not bad idea, but you have to know that in this case the child view (the one that you inflate) will not be able to measure it self correctly in some cases, because it does not know anything about the parent. In some of the cases it will work but in some wouldn't. – Stoycho Andreev Feb 01 '18 at 16:03
  • 1
    Note that this might cause the viewholders theme to not be resolved correctly. – SpaceBison Oct 16 '18 at 08:37
15

I got this message while trying to commit a fragment using attach to root to true instead of false, like so:

return inflater.inflate(R.layout.fragment_profile, container, true)

After doing:

return inflater.inflate(R.layout.fragment_profile, container, false)

It worked.

Taier
  • 2,109
  • 12
  • 22
Hudson Pereira
  • 1,066
  • 8
  • 13
15

You must first remove the child view from its parent.

If your project is in Kotlin, your solution will look slightly different than Java. Kotlin simplifies casting with as?, returning null if left side is null or cast fails.

(childView.parent as? ViewGroup)?.removeView(childView)
newParent.addView(childView)

Kotlin Extension Solution

If you need to do this more than once, add this extension to make your code more readable.

childView.removeSelf()

fun View?.removeSelf() {
    this ?: return
    val parentView = parent as? ViewGroup ?: return
    parentView.removeView(this)
}

It will safely do nothing if this View is null, parent view is null, or parent view is not a ViewGroup

Gibolt
  • 42,564
  • 15
  • 187
  • 127
9

frameLayout.addView(bannerAdView); <----- if you get error on this line the do like below..

if (bannerAdView.getParent() != null)

  ((ViewGroup) bannerAdView.getParent()).removeView(bannerAdView);

   frameLayout.addView(bannerAdView);     <------ now added view
Mayur Dabhi
  • 3,607
  • 2
  • 14
  • 25
6

Simplified in KOTLIN

(viewToRemove.parent as? ViewGroup)?.removeView(viewToRemove)

Juan Mendez
  • 2,658
  • 1
  • 27
  • 23
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
5

If other solution is not working like:

View view = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, false);

check for what are you returning from onCreateView of fragment is it single view or viewgroup? in my case I had viewpager on root of xml of fragment and I was returning viewpager, when i added viewgroup in layout i didnt updated that i have to return viewgroup now, not viewpager(view).

blackHawk
  • 6,047
  • 13
  • 57
  • 100
5

My error was define the view like this:

view = inflater.inflate(R.layout.qr_fragment, container);

It was missing:

view = inflater.inflate(R.layout.qr_fragment, container, false);
Zoe
  • 27,060
  • 21
  • 118
  • 148
Pedro Molina
  • 1,101
  • 12
  • 12
5

In my case it happens when i want add view by parent to other view

View root = inflater.inflate(R.layout.single, null);
LinearLayout lyt = root.findViewById(R.id.lytRoot);
lytAll.addView(lyt);  // ->  crash

you must add parent view like this

View root = inflater.inflate(R.layout.single, null);
LinearLayout lyt = root.findViewById(R.id.lytRoot);
lytAll.addView(root);
Maysam R
  • 915
  • 10
  • 12
5

If you're using ViewBinding, make sure you're referring to the right binding!

I had this issue when I was trying to inflate a custom dialog from within an activity:

AlertDialog.Builder builder = new AlertDialog.Builder(this);

final AlertBinding alertBinding = AlertBinding.inflate(LayoutInflater.from(this), null, false);

builder.setView(binding.getRoot()); // <--- I was using binding (which is my Activity's binding), instead of alertBinding.
Ricardo Yubal
  • 374
  • 3
  • 8
4

In my case, I had id named as "root" for constraint layout, which was conflicting the existing parent root id.

Try to change the id.

<androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/root"  //<--It should not named as root.
            android:layout_width="match_parent"
            android:layout_height="match_parent"

</androidx.constraintlayout.widget.ConstraintLayout>
  • And I added a ScrollView around my layout, which now was the new root which I should have named so. – rwst Feb 21 '22 at 18:14
3

In my case the problem was caused by the fact that I was inflating parent View with <merge> layout. In this case, addView() caused the crash.

View to_add = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, true);
// parent_layout.addView(to_add); // THIS CAUSED THE CRASH

Removing addView() helped to solve the problem.

soshial
  • 5,906
  • 6
  • 32
  • 40
3

The code below solved it for me:

@Override
public void onDestroyView() {
    if (getView() != null) {
        ViewGroup parent = (ViewGroup) getView().getParent();
        parent.removeAllViews();
    }
    super.onDestroyView();
}

Note: The error was from my fragment class and by overriding the onDestroy method like this, I could solve it.

Pierre.Vriens
  • 2,117
  • 75
  • 29
  • 42
therealak12
  • 1,178
  • 2
  • 12
  • 26
3

My problem is related to many of the other answers, but a little bit different reason for needing to make the change... I was trying to convert an Activity to a Fragment. So I moved the inflate code from onCreate to onCreateView, but I forgot to convert from setContentView to the inflate method, and the same IllegalStateException brought me to this page.

I changed this:

binding = DataBindingUtil.setContentView(requireActivity(), R.layout.my_fragment)

to this:

binding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false)

That solved the problem.

Daniel Gibby
  • 101
  • 6
3

You just need to pass attachToRoot parameter false.

mBinding = FragmentCategoryBinding.inflate(inflater, container, false)
Yazdan Ilyas
  • 374
  • 4
  • 8
3

This is how I do my custom dialog

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
android.view.View views = getLayoutInflater().inflate(layout_file, null, false);

builder.setView(views);
dialog = builder.create();
dialog.show();

I changed it into this and its works for me, I hope this helps

Dialog dialog = new Dialog(MainActivity.this);
dialog.setContentView(layout_file);
dialog.show();
2

check if you already added the view

if (textView.getParent() == null)
    layout.addView(textView);
Dan Alboteanu
  • 9,404
  • 1
  • 52
  • 40
2
if(tv!= null){
    ((ViewGroup)tv.getParent()).removeView(tv); // <- fix
}
Zoe
  • 27,060
  • 21
  • 118
  • 148
2

I was facing the same error, and look what I was doing. My bad, I was trying to add the same view NativeAdView to the multiple FrameLayouts, resolved by creating a separate view NativeAdView for each FrameLayout, Thanks

Ali Tamoor
  • 896
  • 12
  • 18
2

In my case I was accidentally returning a child view from within Layout.onCreateView() as shown below:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v= inflater.inflate(R.layout.activity_deliveries, container, false);
    RecyclerView rv  = (RecyclerView) v.findViewById(R.id.deliver_list);
    
    return rv; // <- here's the issue
}

The solution was to return the parent view (v) instead of the child view (rv).

Miguel Tomás
  • 1,714
  • 1
  • 13
  • 23
1

I found another fix:

if (mView.getParent() == null) {
                    myDialog = new Dialog(MainActivity.this);
                    myDialog.setContentView(mView);
                    createAlgorithmDialog();
                } else {
                    createAlgorithmDialog();
                }

Here i just have an if statement check if the view had a parent and if it didn't Create the new dialog, set the contentView and show the dialog in my "createAlgorithmDialog()" method.

This also sets the positive and negative buttons (ok and cancel buttons) up with onClickListeners.

1

In my case, I had an adapter which worked with a recyclerView, the items that were being passed to the adapter were items with their own views.

What was required was just a LinearLayout to act as a container for every item passed, so what I was doing was to grab the item in the specified position inside onBindViewHolder then add it to the LinearLayout, which was then displayed.

Checking the basics in docs,

When an item scrolls off the screen, RecyclerView doesn't destroy its view

Therefore, with my items, when I scroll towards a direction, then change towards the opposite direction - fast, the racently displayed items have not been destroyed, meaning, the items are still associated with the LinearLayout container, then on my end, I'm trying to attach to another container, which ends up with a child having a parent already.

My solution was to check if the specified item has a parent, if it has, I assign it to a variable, then call parentVar.removeView(item), then assign the new parent.

Here's the sample code(Problematic Adapter):

override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) {

    holder.linearLayoutContainer.removeAllViewsInLayout()

    val questionWidget: QuestionWidget =
        dataSource[position]

    questionWidget.setValueChangedListener(this)

    holder.linearLayoutContainer.addView(questionWidget)/*addView throws error once in a while*/
    

}


inner class QuestionWidgetViewHolder(mView: View) : 

RecyclerView.ViewHolder(mView) {
        val linearLayoutContainer: LinearLayout =
            mView.findViewById(R.id.custom_question_widget_container)

}

Content of the R.id.custom_question_widget_container:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/custom_question_widget_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:orientation="vertical"
    android:padding="10dp" />

So, the questionWidget seems to have been retaining the parent for almost 4 steps outside visibility, and when I scroll to the opposite direction fast, I would encounter it still with its parent, then I'm attempting to add it to another container.

Here's the fix - option 1:

        override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) {
    
            holder.linearLayoutContainer.removeAllViewsInLayout()
    
            val questionWidget: QuestionWidget =
                dataSource[position]
    
            questionWidget.setValueChangedListener(this)
    
       val initialWidgetParent : ViewParent? = questionWidget.parent

//attempt to detach from previous parent if it actually has one
        (initialWidgetParent as? ViewGroup)?.removeView(questionWidget)

        holder.linearLayoutContainer.addView(questionWidget)
            
    
        }

Another better solution - option 2:

        override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) {
    
            holder.linearLayoutContainer.removeAllViewsInLayout()
    
            val questionWidget: QuestionWidget =
                dataSource[position]
    
            questionWidget.setValueChangedListener(this)
    
        val initialWidgetParent : ViewParent? = questionWidget.parent
//if it's in a parent container already, just ignore adding it to a view, it's already visible

       if(initialWidgetParent == null) {
           holder.linearLayoutContainer.addView(questionWidget)
       }
            
    
        }

Actually, it's much of asking the child if it has a parent before adding it to a parent.

KE Keronei
  • 323
  • 2
  • 8
1

I tried all the things that you guys suggested, with no luck.

But, I managed to fix it by moving all my binding initializations from onCreate to onCreateView.

onCreate(){
        binding = ScreenTicketsBinding.inflate(layoutInflater)
}

MOVE TO

onCreateView(...){
            binding = ScreenTicketsBinding.inflate(layoutInflater) 
}
ReevoXFR
  • 51
  • 1
1

If you are working with MaterialAlertDialog, this worked for me:

(yourChildView.parent as? ViewGroup)?.removeView(yourChildView)
tumi
  • 11
  • 4
0

You can use this methode to check if a view has children or not .

public static boolean hasChildren(ViewGroup viewGroup) {
    return viewGroup.getChildCount() > 0;
 }
Sofiane Majdoub
  • 826
  • 1
  • 9
  • 12
0

My case was different the child view already had a parent view i am adding the child view inside parent view to different parent. example code below

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/lineGap"
>
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@color/black1"
    android:layout_gravity="center"
    android:gravity="center"
    />

</LinearLayout>

And i was inflating this view and adding to another LinearLayout, then i removed the LinaarLayout from the above layout and its started working

below code fixed the issue:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:gravity="center"
   android:textColor="@color/black1" />
Irfan
  • 956
  • 9
  • 16
0

It happened with me when I was using Databinding for Activity and Fragments.

For fragment - in onCreateView we can inflate the layout in traditional way using inflater. and in onViewCreated method, binding object can be updated as

 binding = DataBindingUtil.getBinding<FragmentReceiverBinding>(view) as FragmentReceiverBinding

It solved my issue

Kaveri
  • 151
  • 1
  • 6
0

In my case, I was doing this (wrong):

...
TextView content = new TextView(context);
for (Quote quote : favQuotes) {
  content.setText(quote.content);
...

instead of (good):

...
for (Quote quote : favQuotes) {
  TextView content = new TextView(context);
  content.setText(quote.content);
...
Inam Ul Huq
  • 732
  • 7
  • 8
0

I think the proper answer to your problem is to initialize the Views inside your runnable function:

private void ConsoleWindow(){
        runOnUiThread(new Runnable(){

     @Override
     public void run(){
        tv = new TextView(getApplicationContext()); 
        et = new EditText(getApplicationContext());

        // MY LAYOUT:
        setContentView(R.layout.activity_console);
        // LINEAR LAYOUT
        LinearLayout layout=new LinearLayout(getApplicationContext());
        layout.setOrientation(LinearLayout.VERTICAL);
        setContentView(layout);

        // TEXTVIEW
        layout.addView(tv); //  <==========  ERROR IN THIS LINE DURING 2ND RUN
        // EDITTEXT
        et.setHint("Enter Command");
        layout.addView(et);
        }
    }
}

So what it does is that it attach the views on your first run and store that on your allocated memory. so when you run it the second time you no longer have the available memory for that view, hence it require you to remove the view first before you try to attach it again.

So if is inside your function it will always create an instance of those views.

-1

If in your XML you have layout with id "root" It`s problem, just change id name

Azarnoy
  • 167
  • 1
  • 1
  • 14