ชายด์ที่ระบุมีพาเรนต์แล้ว คุณต้องเรียก removeView () บนแม่ของเด็กก่อน (Android)


164

ฉันต้องสลับระหว่างเลย์เอาต์สองครั้งบ่อยๆ ข้อผิดพลาดเกิดขึ้นในเค้าโครงที่โพสต์ด้านล่าง

เมื่อเลย์เอาต์ของฉันถูกเรียกครั้งแรกจะไม่มีข้อผิดพลาดเกิดขึ้นและทุกอย่างก็ดี เมื่อฉันเรียกเลย์เอาต์ที่แตกต่าง (อันที่ว่างเปล่า) และหลังจากนั้นเรียกเลย์เอาต์ของฉันอีกเป็นครั้งที่สองมันจะพ่นข้อผิดพลาดต่อไปนี้:

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

รหัสรูปแบบของฉันมีลักษณะดังนี้:

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

ฉันรู้ว่าคำถามนี้ถูกถามมาก่อน แต่มันไม่ได้ช่วยในกรณีของฉัน


1
สำหรับคนที่ได้รับข้อผิดพลาดเดียวกัน: ตรวจสอบให้แน่ใจว่าคุณเพิ่มองค์ประกอบที่ถูกต้อง ช่วยบอกว่าคุณต้องเพิ่มแต่คุณเพิ่มLinearLayout TextViewดังนั้นแก้ไข
นักพัฒนา

เมื่อใช้ android databinding ไม่ควรประกาศมุมมองด้วย id 'root' จะทำให้เกิดข้อผิดพลาดเดียวกัน
Mohammad Reza Khahani

สำหรับผู้ใช้TranstitionManager.beginDelayedTransitionโปรดตรวจสอบคำตอบ
mochadwi

คำตอบ:


354

ข้อความแสดงข้อผิดพลาดระบุสิ่งที่คุณควรทำ

// TEXTVIEW
if(tv.getParent() != null) {
    ((ViewGroup)tv.getParent()).removeView(tv); // <- fix
}
layout.addView(tv); //  <==========  ERROR IN THIS LINE DURING 2ND RUN
// EDITTEXT

57

เพียงส่งอาร์กิวเมนต์ Attachtoroot เป็นเท็จ

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

2
สำหรับฉันนี่เป็นเป้า เลย์เอาต์ จำกัด สำหรับรายการใน recycleView ซึ่งมีการเปลี่ยนแปลงข้อ จำกัด แบบไดนามิกตามรันไทม์ ConstraintSet.apply ใช้งานไม่ได้จนกว่าอันนี้ ดังนั้นแพร่กระจายผู้ปกครองใน onCreateViewHolder แต่ส่งเท็จเป็นพารามิเตอร์ aditional เพื่อขยาย
miroslavign

3
นี่คือทางออกที่ถูกต้อง! ดังที่ @Sniper กล่าวว่าการส่งค่า null ไปยังพารามิเตอร์ที่สองสามารถทำให้มุมมองลูกไม่จัดแนวตัวเองตามที่ควรจะเป็นกับพาเรนต์ OTOH ผ่านผู้ปกครองสามารถทำให้เกิดข้อผิดพลาดในคำถาม ดังนั้นattachToRoot= false เป็นวิธีที่จะไป
Vassilis

วิธีที่ถูกต้องในการทำมัน
Rowland Mtetezi

50

ฉันมาที่นี่เพื่อค้นหาข้อผิดพลาดกับ recyclerview ของฉัน แต่วิธีแก้ไขไม่ได้ผล (ชัด) ฉันได้เขียนสาเหตุและวิธีแก้ปัญหาในกรณีที่มีการรีไซเคิล หวังว่าจะช่วยใครซักคน

ข้อผิดพลาดเกิดขึ้นถ้าในonCreateViewHolder()วิธีการดังต่อไปนี้มีการปฏิบัติตาม:

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

แต่มันควรจะเป็น

return new VH(layoutInflater.inflate(R.layout.single_row, null));

15
บางครั้งคำตอบแบบสุ่มซึ่งไม่ตรงกับคำตอบของคำถามที่ถามช่วยคนอื่น สิ่งนี้ใช้ได้สำหรับฉัน ขอบคุณ!
Ajith Memana

1
ดีมาก! และนั่นเป็นเหตุผลว่าทำไมคนส่วนใหญ่ควรผ่าน "null" เป็นพารามิเตอร์ 2d ใน inflaters ขอบคุณ
superUser

4
การส่งค่า Null ไปยังพารามิเตอร์ที่รับผิดชอบสำหรับผู้ปกครองนั้นไม่ใช่ความคิดที่เลวร้าย แต่คุณต้องรู้ว่าในกรณีนี้มุมมองของเด็ก (มุมมองที่คุณขยายตัว) จะไม่สามารถวัดตัวเองได้อย่างถูกต้องในบางกรณีเพราะมันไม่ รู้อะไรเกี่ยวกับผู้ปกครอง ในบางกรณีมันจะทำงาน แต่ในบางกรณีจะไม่
Stoycho Andreev

1
โปรดทราบว่านี่อาจทำให้ชุดรูปแบบของวิวเวอร์แก้ไขไม่ถูกต้อง
SpaceBison

13

ฉันได้รับข้อความนี้ขณะที่พยายามส่งแฟรกเมนต์โดยใช้ Attach to root เป็น True แทนที่จะเป็น false เช่น:

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

หลังจากทำ:

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

มันได้ผล


5

หากวิธีการอื่นไม่ทำงานเช่น:

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

ตรวจสอบสิ่งที่คุณส่งคืนจาก onCreateView ของแฟรกเมนต์คือมุมมองเดี่ยวหรือกลุ่มมุมมอง? ในกรณีของฉันฉันมี viewpager ที่ root ของ xml ของ fragment และฉันกลับไปที่ viewpager เมื่อฉันเพิ่ม viewgroup ใน layout ฉันไม่ได้อัพเดตว่าต้องคืนวิวกรุ๊ปตอนนี้ไม่ใช่ viewpager (view)


5

frameLayout.addView (bannerAdView); <----- หากคุณได้รับข้อผิดพลาดในบรรทัดนี้ให้ทำดังนี้

if (bannerAdView.getParent() != null)

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

   frameLayout.addView(bannerAdView);     <------ now added view

4

ข้อผิดพลาดของฉันถูกกำหนดมุมมองเช่นนี้:

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

มันหายไป:

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

3

ในกรณีของฉันปัญหาเกิดจากการที่ฉันขยายมุมมองพาเรนต์ด้วย<merge>เค้าโครง ในกรณีนี้addView()ทำให้เกิดความผิดพลาด

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

การนำออกaddView()ช่วยในการแก้ปัญหา



2

รหัสด้านล่างแก้ไขให้ฉัน:

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

หมายเหตุ: ข้อผิดพลาดมาจากคลาสแฟรกเมนต์ของฉันและโดยการแทนที่เมธอด onDestroy เช่นนี้ฉันสามารถแก้ไขได้



2

ในกรณีของฉันมันเกิดขึ้นเมื่อฉันต้องการเพิ่มมุมมองโดยผู้ปกครองไปยังมุมมองอื่น

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

คุณต้องเพิ่มมุมมองพาเรนต์แบบนี้

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

2

คุณต้องลบมุมมองชายด์ออกจากพาเรนต์ก่อน

หากโครงการของคุณอยู่ใน Kotlin โซลูชันของคุณจะดูแตกต่างจาก Java เล็กน้อย Kotlin ลดความยุ่งยากในการร่ายด้วยการas?คืนค่า null หากด้านซ้ายเป็นโมฆะหรือการร่ายล้มเหลว

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

โซลูชั่นเสริม Kotlin

หากคุณต้องการทำมากกว่าหนึ่งครั้งให้เพิ่มส่วนขยายนี้เพื่อให้โค้ดของคุณอ่านง่ายขึ้น

childView.removeSelf()

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

มันจะไม่ทำอะไรอย่างปลอดภัยหากมุมมองนี้เป็นโมฆะมุมมองพาเรนต์เป็นโมฆะหรือมุมมองพาเรนต์ไม่ใช่วิวเวอร์


1

ฉันพบวิธีแก้ไขอื่น:

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

ที่นี่ฉันเพียงแค่มีคำสั่ง if ตรวจสอบว่ามุมมองมีพาเรนต์หรือไม่และไม่ได้สร้างไดอะล็อกใหม่ให้ตั้งค่า contentView และแสดงไดอะล็อกในเมธอด "createAlgorithmDialog ()" ของฉัน

นอกจากนี้ยังเป็นการตั้งค่าปุ่มบวกและลบ (ปุ่มตกลงและปุ่มยกเลิก) ด้วย onClickListeners


0

คุณสามารถใช้ methode นี้เพื่อตรวจสอบว่ามุมมองมีลูกหรือไม่

public static boolean hasChildren(ViewGroup viewGroup) {
    return viewGroup.getChildCount() > 0;
 }

0

เคสของฉันแตกต่างจากมุมมองเด็กมีมุมมองพาเรนต์อยู่แล้วฉันกำลังเพิ่มมุมมองเด็กภายในมุมมองพาเรนต์ไปยังพาเรนต์อื่น รหัสตัวอย่างด้านล่าง

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

และฉันขยายมุมมองนี้และเพิ่มลงใน LinearLayout อีกอันจากนั้นฉันลบ LinaarLayout ออกจากเค้าโครงด้านบนและเริ่มทำงาน

รหัสด้านล่างแก้ไขปัญหา:

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

0

ปัญหาของฉันเกี่ยวข้องกับคำตอบอื่น ๆ อีกมากมาย แต่เหตุผลที่แตกต่างกันเล็กน้อยสำหรับการเปลี่ยนแปลง ... ฉันพยายามแปลงกิจกรรมเป็นชิ้นส่วน ดังนั้นฉันจึงย้ายรหัส inflate จาก onCreate เป็น onCreateView แต่ฉันลืมที่จะแปลงจาก setContentView ไปเป็นวิธีพองตัวและ IllegalStateException เดียวกันก็พาฉันมาที่หน้านี้

ฉันเปลี่ยนสิ่งนี้:

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

สำหรับสิ่งนี้:

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

ที่แก้ไขปัญหา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.