Rufen Sie zuerst removeView () auf dem Elternteil des Kindes auf


138

Zunächst ein kleiner Hintergrund:

Ich habe ein Layout in einer Bildlaufansicht. Wenn der Benutzer auf dem Bildschirm einen Bildlauf durchführt, wird zunächst ein Bildlauf in der Bildlaufansicht durchgeführt. Nach einer gewissen Anzahl von Bildläufen sollte ich jedoch den Bildlauf in der Bildlaufansicht deaktivieren und den "Bildlauffokus" auf eine Webansicht innerhalb des untergeordneten Layouts verschieben. Auf diese Weise bleibt die Bildlaufansicht erhalten und alle Bildlaufereignisse gelangen in die darin enthaltene Webansicht.

Wenn eine Bildlaufschwelle erreicht ist, entferne ich für eine Lösung das untergeordnete Layout aus der Bildlaufansicht und füge es in das übergeordnete Element der Bildlaufansicht ein (und mache die Bildlaufansicht unsichtbar).

// Remove the child view from the scroll view
scrollView.removeView(scrollChildLayout);

// Get scroll view out of the way
scrollView.setVisibility(View.GONE);

// Put the child view into scrollview's parent view
parentLayout.addView(scrollChildLayout);

Allgemeine Idee: (-> bedeutet enthält)

Vorher: Elternlayout -> Bildlaufansicht -> scrollChildLayout

Nachher: ​​parentLayout -> scrollChildLayout

Der obige Code gibt mir diese Ausnahme:

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:1976)
           at android.view.ViewGroup.addView(ViewGroup.java:1871)
           at android.view.ViewGroup.addView(ViewGroup.java:1828)
           at android.view.ViewGroup.addView(ViewGroup.java:1808)

Weißt du was los ist? Ich rufe eindeutig removeView auf dem übergeordneten Element auf.

Antworten:


289

Lösung:

((ViewGroup)scrollChildLayout.getParent()).removeView(scrollChildLayout);
//scrollView.removeView(scrollChildLayout);

Verwenden Sie das untergeordnete Element, um einen Verweis auf das übergeordnete Element abzurufen. Verwandeln Sie das übergeordnete Element in eine ViewGroup, damit Sie Zugriff auf die removeView-Methode erhalten und diese verwenden können.

Vielen Dank an @Dongshengcn für die Lösung


1
Die Lösung ist richtig, aber warum würde "scollView.removeView (scrollChildLayout)" nicht funktionieren? Sollten die beiden Zeilen nicht gleich sein?
andrea.rinaldi

@ andrea.spot, scheint zu scrollView.removeView(scrollChildLayout)versuchen, das Kind von scrollView zu entfernen, nicht das scrollView selbst von seiner übergeordneten ViewGroup zu entfernen
Dmitry Gryazin

1
@ user25 Ich weiß, dass dies möglicherweise zu spät für dich ist, aber die Nullzeiger-Ausnahme ist darauf zurückzuführen, dass das übergeordnete Element leer ist, was bedeutet, dass die Ansicht keinem übergeordneten Element zugeordnet ist. Da am Anfang kein Elternteil vorhanden ist, muss nichts entfernt werden. if (mAdView.getParent()!=null) ((ViewGroup) mAdView.getParent()).removeView(mAdView);
Tarek

@kasgoku kannst du mir bitte helfen. Ich versuche, ein Fragment im relativen Layout auf einen Knopfdruck zu zeigen. Es wird jedoch der Fehler "Das angegebene Kind hat bereits ein Elternteil. Sie müssen removeView () zuerst für das Elternteil des Kindes aufrufen." wie kann man das lösen?
Imranrana07

Ich habe diesen Fehler auch in meiner Anwendung erhalten, die eine der Chat-Anwendungen ist. Ich versuche dieses Problem zu lösen. Dieses Symbol wird entfernt, aber ich benötige das Symbol erneut, um Bilder hochzuladen. ( stackoverflow.com/q/58117428/10971384 ) Dies ist mein
Fragenlink

21

Versuchen Sie zuerst, scrollChildLayout aus der übergeordneten Ansicht zu entfernen.

scrollview.removeView(scrollChildLayout)

Oder entfernen Sie das gesamte untergeordnete Element aus der übergeordneten Ansicht und fügen Sie es erneut hinzu.

scrollview.removeAllViews()

Ich rufe removeView bereits im Code in meiner obigen Frage auf. removeAllViews () funktioniert auch nicht.
Kasgoku

Sind Sie sicher, dass es sich um das übergeordnete Element handelt, das Sie removeView () / removeAllViews () aufrufen? Ich mache sehr ähnliche Dinge und es hat für mich funktioniert.
Dongshengcn

4
Sie können sich das übergeordnete Element ansehen, indem Sie Folgendes anzeigen: view.getParent () developer.android.com/reference/android/view/…
dongshengcn

@dongshengcn kannst du mir bitte helfen. Ich versuche, ein Fragment im relativen Layout auf einen Knopfdruck zu zeigen. Es wird jedoch der Fehler "Das angegebene Kind hat bereits ein Elternteil. Sie müssen removeView () zuerst für das Elternteil des Kindes aufrufen." wie kann man das lösen?
Imranrana07

19

In onCreate mit Aktivität oder in onCreateView mit Fragment.

 if (view != null) {
    ViewGroup parent = (ViewGroup) view.getParent();
    if (parent != null) {
        parent.removeView(view);
    }
}
try {
    view = inflater.inflate(R.layout.fragment_main, container, false);
} catch (InflateException e) {

}

15

Ok, nenn mich paranoid, aber ich schlage vor:

  final android.view.ViewParent parent = view.getParent ();

  if (parent instanceof android.view.ViewManager)
  {
     final android.view.ViewManager viewManager = (android.view.ViewManager) parent;

     viewManager.removeView (view);
  } // if

Casting ohne instanceofscheint einfach falsch. Und (danke IntelliJ IDEA, dass Sie es mir gesagt haben) removeViewist Teil der ViewManagerBenutzeroberfläche. Und man sollte nicht in eine konkrete Klasse werfen, wenn eine perfekt geeignete Schnittstelle verfügbar ist.


3

Alles, was Sie tun müssen, ist post () ein Runnable, das addView () ausführt.


Hat nicht funktioniert. Folgendes habe ich versucht: scrollView.removeView (scrollChildLayout); scrollView.setVisibility (View.GONE); parentLayout.post (new Runnable () {@Override public void run () {parentLayout.addView (scrollChildLayout);}});
Kasgoku

1

Ich habe parentView.removeView (childView) aufgerufen und childView wurde immer noch angezeigt. Schließlich stellte ich fest, dass eine Methode zweimal ausgelöst wurde, und fügte die childView zweimal der parentView hinzu.

Verwenden Sie also parentView.getChildCount (), um zu bestimmen, wie viele untergeordnete Elemente das übergeordnete Element hat, bevor Sie eine Ansicht hinzufügen und danach. Wenn das untergeordnete Element zu oft hinzugefügt wird, wird das oberste untergeordnete Element entfernt und die Kopie childView bleibt erhalten. Dies sieht so aus, als würde removeView auch dann funktionieren, wenn dies der Fall ist.

Außerdem sollten Sie View.GONE nicht verwenden, um eine Ansicht zu entfernen. Wenn es wirklich entfernt wurde, müssen Sie es nicht verstecken, sonst ist es immer noch da und Sie verstecken es nur vor sich selbst :(


1

In meinem Fall habe ich BaseFragment und alle anderen Fragmente erben davon.

Also war meine Lösung, diese Zeilen in der OnDestroyView()Methode hinzuzufügen

@Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    if (mRootView == null)
    {
        mRootView = (inflater == null ? getActivity().getLayoutInflater() : inflater).inflate(mContentViewResourceId, container, false);
    }
....////
}

@Override
public void onDestroyView()
{
    if (mRootView != null)
    {
        ViewGroup parentViewGroup = (ViewGroup) mRootView.getParent();

        if (parentViewGroup != null)
        {
            parentViewGroup.removeAllViews();
        }
    }

    super.onDestroyView();
}

Haben Sie versucht, das nächste Fragment auszuwählen und zu schnell zurückzudrücken?
iamthevoid

1

Kotlin-Lösung

Kotlin vereinfacht das übergeordnete Casting mit as?und gibt null zurück, wenn die linke Seite null ist oder das Casting fehlschlägt.

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

Kotlin-Erweiterungslösung

Wenn Sie dies noch weiter vereinfachen möchten, können Sie diese Erweiterung hinzufügen.

childView.removeSelf()

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

Es wird sicher nichts tun, wenn diese Ansicht null ist, die übergeordnete Ansicht null ist oder die übergeordnete Ansicht keine ViewGroup ist

HINWEIS: Wenn Sie auch untergeordnete Ansichten sicher von den übergeordneten Ansichten entfernen möchten, fügen Sie Folgendes hinzu:

fun ViewGroup.removeViewSafe(toRemove: View) {
    if (contains(toRemove)) removeView(toRemove)
}

0

Sie können dies auch tun, indem Sie überprüfen, ob die indexOfView-Methode von View verwendet wird, wenn die indexOfView-Methode -1 zurückgibt.

DetectViewFromParent (v) von ViewGroup; gefolgt von removeDetachedView von ViewGroup (v, true / false);


0

Was ich falsch gemacht habe, also habe ich diesen Fehler bekommen, ist, dass ich kein dynamisches Layout instanziiert und Kinder hinzugefügt habe, also habe ich diesen Fehler bekommen


0

Hier ist meine Lösung.

Nehmen wir an, Sie haben zwei TextViewsund setzen sie auf einen LinearLayout(benannten ll). Du wirst das LinerLayoutauf einen anderen setzen LinerLayout.

< lm Linear Layout> 
       < ll Linear Layout> 
             <name Text view>
             </name Text view>
             <surname Text view>
             </surname Text view>
       </ll Linear Layout> 
</lm Linear Layout> 

Wenn Sie diese Struktur erstellen möchten, müssen Sie übergeordnetes Element als Vererbung angeben.

Wenn Sie es in einer onCreateMethode verwenden möchten, thisreicht dies aus.

Ansonsten ist hier Solition:

LinerLayout lm = new LinearLayout(this); // You can use getApplicationContext() also
LinerLayout ll = new LinearLayout(lm.getContext());
TextView name = new TextView(ll.getContext());
TextView surname = new TextView(ll.getContext());
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.