Podane dziecko ma już rodzica. Najpierw musisz wywołać metodę removeView () na rodzicu dziecka (Android)

164

Muszę często przełączać się między dwoma układami. Błąd występuje w układzie zamieszczonym poniżej.

Kiedy mój układ jest wywoływany po raz pierwszy, nie ma żadnego błędu i wszystko jest w porządku. Kiedy następnie wywołuję inny układ (pusty), a następnie wywołuję mój układ po raz drugi, generuje następujący błąd:

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

Mój kod układu wygląda następująco:

    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);
        }
    }
}

Wiem, że to pytanie zostało już zadane, ale w moim przypadku nie pomogło.

blackst0ne
źródło
1
Tylko dla kogoś, kto ma ten sam błąd: upewnij się, że dodajesz poprawny element. Powiedzmy, że musisz dodać, LinearLayoutale dodajesz TextView. Więc napraw to.
Deweloper
gdy używanie androida databinding nie powinno deklarować widoku z identyfikatorem „root”, powoduje ten sam błąd.
Mohammad Reza Khahani
dla korzystających TranstitionManager.beginDelayedTransition, proszę sprawdzić moją odpowiedź tutaj
mochadwi

Odpowiedzi:

354

Komunikat o błędzie mówi, co należy zrobić.

// TEXTVIEW
if(tv.getParent() != null) {
    ((ViewGroup)tv.getParent()).removeView(tv); // <- fix
}
layout.addView(tv); //  <==========  ERROR IN THIS LINE DURING 2ND RUN
// EDITTEXT
Zielony
źródło
56

po prostu przekaż argument attachtoroot jako fałsz

View view = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, false);
Studia Pb
źródło
2
Dla mnie to był strzał w dziesiątkę. Układ ograniczeń dla elementów w widoku recycleView, dynamicznie zmieniający ograniczenia na podstawie czasu wykonywania. ConstraintSet.apply nie działał, aż do tego. Tak więc propaguj rodzica w onCreateViewHolder, ale wyślij false jako dodatkowy parametr do inflacji.
miroslavign
3
To jest właściwe rozwiązanie! Jak powiedział @Sniper, przekazanie wartości null do drugiego parametru może spowodować, że widok podrzędny nie wyrówna się tak, jak powinien, do swojego rodzica. OTOH przekazanie rodzica może spowodować omawiany błąd. Zatem attachToRoot= fałsz jest drogą do zrobienia.
Vassilis
Właściwy sposób na zrobienie tego.
Rowland Mtetezi
50

Przyszedłem tutaj, aby wyszukać błąd z moim recyklingiem, ale rozwiązanie nie zadziałało (oczywiście). Napisałem przyczynę i rozwiązanie problemu w przypadku przeglądu recyklingowego. Mam nadzieję, że to komuś pomoże.

Błąd jest spowodowany, jeśli zastosowana onCreateViewHolder()zostanie następująca metoda:

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

Zamiast tego powinno być

return new VH(layoutInflater.inflate(R.layout.single_row, null));
suku
źródło
15
Czasami przypadkowe odpowiedzi, które nie są dokładnymi odpowiedziami na zadane pytania, pomagają komuś innemu. To zadziałało dla mnie. Dzięki!
Ajith Memana
1
Fajnie !, i dlatego ludzie w większości przypadków powinni podawać „null” jako parametry 2d w inflatersach. Dzięki.
superUser
4
Podanie wartości Null do parametru odpowiedzialnego za rodzica nie jest złym pomysłem, ale musisz wiedzieć, że w tym przypadku widok potomny (ten, który nadmuchujesz) nie będzie w stanie w niektórych przypadkach poprawnie zmierzyć siebie, bo tak nie jest wiedzieć cokolwiek o rodzicu. W niektórych przypadkach zadziała, ale w innych nie.
Stoycho Andreev
1
Zwróć uwagę, że może to spowodować, że motyw wizjerów nie zostanie poprawnie rozwiązany.
SpaceBison,
13

Otrzymałem tę wiadomość podczas próby zatwierdzenia fragmentu za pomocą dołącz do roota na true zamiast false, na przykład:

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

Po zrobieniu:

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

Zadziałało.

Hudson Pereira
źródło
5

Jeśli inne rozwiązanie nie działa jak:

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

sprawdź, co zwracasz z onCreateView fragmentu, czy jest to pojedynczy widok czy grupa widoków? w moim przypadku miałem viewpager w katalogu głównym xml fragmentu i zwracałem viewpager, kiedy dodałem viewgroup w układzie, nie zaktualizowałem, że muszę teraz zwrócić viewgroup, a nie viewpager (widok).

czarny Jastrząb
źródło
5

frameLayout.addView (bannerAdView); <----- jeśli pojawi się błąd w tej linii, wykonaj poniższe czynności.

if (bannerAdView.getParent() != null)

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

   frameLayout.addView(bannerAdView);     <------ now added view
Mayur Dabhi
źródło
4

Mój błąd polegał na zdefiniowaniu widoku w ten sposób:

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

Brakowało:

view = inflater.inflate(R.layout.qr_fragment, container, false);
Pedro Molina
źródło
3

W moim przypadku problem był spowodowany tym, że nadmuchałem widok rodzica z <merge>układem. W tym przypadku addView()spowodował awarię.

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

Usunięcie addView()pomogło rozwiązać problem.

soshial
źródło
2

sprawdź, czy dodałeś już widok

if (textView.getParent() == null)
    layout.addView(textView);
Dan Alboteanu
źródło
2

Poniższy kod rozwiązał to za mnie:

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

Uwaga: błąd pochodził z mojej klasy fragmentów i przez zastąpienie metody onDestroy w ten sposób mogłem go rozwiązać.

AK 12
źródło
2
if(tv!= null){
    ((ViewGroup)tv.getParent()).removeView(tv); // <- fix
}
Chandan Bera
źródło
2

W moim przypadku dzieje się tak, gdy chcę dodać widok przez rodzica do innego widoku

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

musisz dodać widok rodzica w ten sposób

View root = inflater.inflate(R.layout.single, null);
LinearLayout lyt = root.findViewById(R.id.lytRoot);
lytAll.addView(root);
Maysam R
źródło
2

Najpierw musisz usunąć widok podrzędny z jego elementu nadrzędnego.

Jeśli Twój projekt jest w Kotlinie, Twoje rozwiązanie będzie wyglądać nieco inaczej niż Java. Kotlin upraszcza rzutowanie as?, zwracając wartość null, jeśli lewa strona jest pusta lub rzutowanie się nie powiedzie.

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

Rozszerzenie Kotlin

Jeśli musisz to zrobić więcej niż raz, dodaj to rozszerzenie, aby kod był bardziej czytelny.

childView.removeSelf()

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

Bezpiecznie nic nie zrobi, jeśli ten widok ma wartość null, widok nadrzędny ma wartość null lub widok nadrzędny nie jest grupą widoków

Gibolt
źródło
1

Znalazłem inną poprawkę:

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

Tutaj mam po prostu instrukcję if, która sprawdza, czy widok ma element nadrzędny, a jeśli nie. Utwórz nowe okno dialogowe, ustaw contentView i pokaż okno dialogowe w mojej metodzie „createAlgorithmDialog ()”.

To również ustawia przyciski dodatnie i ujemne (przyciski ok i anuluj) za pomocą onClickListeners.

Joshua Orotayo
źródło
0

Możesz użyć tej metody, aby sprawdzić, czy widok ma dzieci, czy nie.

public static boolean hasChildren(ViewGroup viewGroup) {
    return viewGroup.getChildCount() > 0;
 }
Sofiane Majdoub
źródło
0

Mój przypadek był inny, widok dziecka miał już widok nadrzędny. Dodaję widok podrzędny w widoku rodzica do innego rodzica. przykładowy kod poniżej

<?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>

Nadmuchałem ten widok i dodałem do innego LinearLayout, a następnie usunąłem LinaarLayout z powyższego układu i zaczął działać

poniższy kod naprawił problem:

<?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
źródło
0

Mój problem jest związany z wieloma innymi odpowiedziami, ale trochę inny powód, dla którego muszę wprowadzić zmianę ... Próbowałem przekonwertować działanie na fragment. Więc przeniosłem kod inflate z onCreate do onCreateView, ale zapomniałem przekonwertować z setContentView na metodę inflate i ten sam IllegalStateException przeniósł mnie na tę stronę.

Zmieniłem to:

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

do tego:

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

To rozwiązało problem.

Daniel Gibby
źródło