Die LayoutInflater.inflate
Dokumentation ist mir über den Zweck des attachToRoot
Parameters nicht genau klar .
attachToRoot : Soll die aufgeblasene Hierarchie an den Root-Parameter angehängt werden? Wenn false, wird root nur verwendet, um die richtige Unterklasse von LayoutParams für die Root-Ansicht in XML zu erstellen.
Könnte jemand bitte detaillierter erklären, insbesondere was die Stammansicht ist, und vielleicht ein Beispiel für eine Änderung des Verhaltens zwischen true
und false
Werten zeigen?
android
android-layout
android-view
layout-inflater
Jeff Axelrod
quelle
quelle
Antworten:
JETZT ODER NICHT JETZT
Der Hauptunterschied zwischen dem "dritten" Parameter attachToRoot, der wahr oder falsch ist, ist dieser.
true: Fügen Sie die untergeordnete Ansicht JETZT dem übergeordneten Element hinzu.
false: Fügen Sie die untergeordnete Ansicht dem übergeordneten Element NOT NOW hinzu .
Fügen Sie es später hinzu. `
Das ist später, wenn Sie für z
parent.addView(childView)
Ein häufiges Missverständnis ist, dass die untergeordnete Ansicht nicht zum übergeordneten Element hinzugefügt wird, wenn der Parameter attachToRoot false ist. FALSCH
In beiden Fällen wird die untergeordnete Ansicht zur übergeordneten Ansicht hinzugefügt. Es ist nur eine Frage der Zeit .
ist äquivalent zu
A BIG NO-NO
Sie sollten attachToRoot niemals als true übergeben, wenn Sie nicht dafür verantwortlich sind, die untergeordnete Ansicht dem übergeordneten Element hinzuzufügen.
ZB beim Hinzufügen eines Fragments
Wenn Sie den dritten Parameter als true übergeben, erhalten Sie wegen dieses Typen die IllegalStateException.
Da Sie das untergeordnete Fragment bereits versehentlich in onCreateView () hinzugefügt haben. Wenn Sie add aufrufen, erfahren Sie, dass die untergeordnete Ansicht bereits zur übergeordneten Ansicht hinzugefügt wurde .
Hier sind Sie nicht für das Hinzufügen von childView verantwortlich, FragmentManager ist dafür verantwortlich. In diesem Fall also immer falsch übergeben.
HINWEIS: Ich habe auch gelesen, dass parentView keine childView touchEvents erhält, wenn attachToRoot false ist. Aber ich habe es nicht getestet.
quelle
FragmentManager
, danke!Wenn der Wert auf true gesetzt ist, wird Ihr Layout beim Aufblasen automatisch zur Ansichtshierarchie der ViewGroup hinzugefügt, die im 2. Parameter als untergeordnetes Element angegeben ist. Wenn der Root-Parameter beispielsweise a war,
LinearLayout
wird Ihre aufgeblasene Ansicht automatisch als untergeordnetes Element dieser Ansicht hinzugefügt.Wenn es auf false gesetzt ist, wird Ihr Layout aufgeblasen, aber nicht an ein anderes Layout angehängt (es wird also nicht gezeichnet, es werden Berührungsereignisse empfangen usw.).
quelle
false
fürattachToRoot
das während meines FragmentsonCreateView
. Dies löste das Problem und noch das Layout des Fragments ist sichtbar und aktiv, trotz Ihrer Antwort. Was ist los hier?true
wird die Ansicht an den 2. Parameter angehängt, der der istcontainer
, aber dann sagen Sie, Fragment wird automatisch von angehängtonCreateView()
, so dass nach meinem Verständnis der dritte Parameter nutzlos ist und gesetzt werden solltefalse
immer?onCreateView
. Wenn Sie weitere Layouts in diese Stammansicht aufblasen oder in einem anderen Kontext (z. B. in einer Aktivität) aufblasen, ist dies hilfreich.Scheint viel Text in den Antworten zu sein, aber kein Code. Deshalb habe ich beschlossen, diese alte Frage mit einem Codebeispiel in mehreren Antworten wiederzubeleben:
Was das im Code tatsächlich bedeutet (was die meisten Programmierer verstehen), ist:
Beachten Sie, dass der vorherige Code das Layout
R.layout.child_view
als untergeordnetes ElementMyCustomLayout
aufgrund vonattachToRoot
param is hinzufügttrue
und die Layoutparameter des übergeordneten Elements genau so zuweist, als ob ich esaddView
programmgesteuert verwenden würde oder als ob ich dies in XML getan hätte:Der folgende Code erläutert das Szenario beim Übergeben
attachRoot
alsfalse
:Im vorherigen Code haben Sie angegeben, dass Sie
myView
ein eigenes Stammobjekt sein möchten und es keinem übergeordneten Objekt zuordnen möchten. Später haben wir es als Teil derLinearLayout
Ansicht hinzugefügt, aber für einen Moment war es eine eigenständige Ansicht (kein übergeordnetes Objekt ).Dasselbe passiert mit Fragmenten. Sie können sie einer bereits vorhandenen Gruppe hinzufügen und Teil dieser Gruppe sein oder einfach die Parameter übergeben:
Um anzugeben, dass es sich um eine eigene Wurzel handelt.
quelle
Die Dokumentation und die beiden vorherigen Antworten sollten ausreichen, nur ein paar Gedanken von mir.
Die
inflate
Methode wird zum Aufblasen von Layoutdateien verwendet. Bei diesen aufgeblasenen Layouts müssen Sie die Möglichkeit haben, sie direkt an ein übergeordnetesViewGroup
Element anzuhängen oder einfach die Ansichtshierarchie aus dieser Layoutdatei aufzublasen und außerhalb der normalen Ansichtshierarchie damit zu arbeiten.Im ersten Fall muss der
attachToRoot
Parameter auf gesetzt werdentrue
(oder viel einfacher dieinflate
Methode verwenden, die eine Layoutdatei und ein übergeordnetes StammverzeichnisViewGroup
(nichtnull
) verwendet). In diesem Fall ist dieView
Rückgabe einfachViewGroup
diejenige, die in der Methode übergeben wurde,ViewGroup
zu der die aufgeblasene Ansichtshierarchie hinzugefügt wird.Bei der zweiten Option
View
ist das zurückgegebene StammverzeichnisViewGroup
aus der Layoutdatei. Wenn Sie sich an unsere letzte Diskussion aus derinclude-merge
Paarfrage erinnern, ist dies einer der Gründe für diemerge
Einschränkung von (wenn eine Layoutdatei mitmerge
als root aufgeblasen wird, müssen Sie ein übergeordnetes Element angeben undattachedToRoot
müssen auf eingestellt seintrue
). Wenn Sie eine Layoutdatei mit dem Root-merge
Tag a hatten undattachedToRoot
auf gesetzt waren, kannfalse
dieinflate
Methode nichts zurückgeben, damerge
sie kein Äquivalent hat. Wie in der Dokumentation angegeben, ist auch dieinflate
Version mitattachToRoot
set tofalse
wichtig, da Sie die Ansichtshierarchie mit der richtigen erstellen könnenLayoutParams
vom Elternteil. Dies ist in einigen Fällen wichtig, insbesondere bei KindernAdapterView
einer Unterklasse vonViewGroup
, für die die festgelegtenaddView()
Methoden nicht unterstützt werden. Ich bin sicher, Sie erinnern sich daran, diese Zeile in dergetView()
Methode verwendet zu haben:Diese Linie stellt sicher , dass der aufgeblasene
R.layout.row_layout
Datei die richtige istLayoutParams
aus derAdapterView
Unterklasse Satz auf seiner WurzelViewGroup
. Wenn Sie dies nicht tun würden, könnten Sie Probleme mit der Layoutdatei haben, wenn das Stammverzeichnis a wäreRelativeLayout
. DieTableLayout/TableRow
haben auch einige spezielle und wichtigeLayoutParams
und Sie sollten sicherstellen, dass die Ansichten in ihnen die richtigen habenLayoutParams
.quelle
Ich selbst war auch verwirrt über das, was war der eigentliche Zweck des
attachToRoot
ininflate
Methode. Nach einigem UI-Studium bekam ich endlich die Antwort:Elternteil:
In diesem Fall handelt es sich um das Widget / Layout, das die Ansichtsobjekte umgibt, die Sie mit findViewById () aufblasen möchten.
attachToRoot:
Hängt die Ansichten an die übergeordnete Ansicht an (schließt sie in die übergeordnete Hierarchie ein), sodass jedes Berührungsereignis, das die Ansichten erhalten, auch an die übergeordnete Ansicht übertragen wird. Jetzt ist es Sache der Eltern, diese Ereignisse zu unterhalten oder zu ignorieren. Wenn sie auf false gesetzt sind, werden sie nicht als direkte untergeordnete Elemente des übergeordneten Elements hinzugefügt, und das übergeordnete Element empfängt keine Berührungsereignisse aus den Ansichten.
Hoffe das klärt die Verwirrung
quelle
Ich habe diese Antwort geschrieben, weil ich selbst nach dem Durchblättern mehrerer StackOverflow-Seiten nicht klar verstehen konnte, was attachToRoot bedeutet. Unten finden Sie die inflate () -Methode in der LayoutInflater-Klasse.
Schauen Sie sich die Datei activity_main.xml , das Layout button.xml und die von mir erstellte Datei MainActivity.java an .
activity_main.xml
button.xml
MainActivity.java
Wenn wir den Code ausführen, wird die Schaltfläche im Layout nicht angezeigt. Dies liegt daran, dass unser Schaltflächenlayout nicht zum Hauptaktivitätslayout hinzugefügt wird, da attachToRoot auf false gesetzt ist.
LinearLayout verfügt über eine addView- Methode (Ansichtsansicht) , mit der Ansichten zu LinearLayout hinzugefügt werden können. Dadurch wird das Schaltflächenlayout zum Hauptaktivitätslayout hinzugefügt und die Schaltfläche wird sichtbar, wenn Sie den Code ausführen.
Entfernen wir die vorherige Zeile und sehen, was passiert, wenn wir attachToRoot auf true setzen.
Wieder sehen wir, dass das Schaltflächenlayout sichtbar ist. Dies liegt daran, dass attachToRoot das aufgeblasene Layout direkt an das angegebene übergeordnete Layout anfügt. Was in diesem Fall root LinearLayout ist. Hier müssen wir die Ansichten nicht manuell hinzufügen, wie wir es im vorherigen Fall mit der Methode addView (Ansichtsansicht) getan haben.
Warum erhalten Benutzer IllegalStateException, wenn attachToRoot für ein Fragment als true festgelegt wird?
Dies liegt daran, dass Sie für ein Fragment bereits angegeben haben, wo Ihr Fragmentlayout in Ihrer Aktivitätsdatei abgelegt werden soll.
Das Add (int parent, Fragment fragment) fügt das Fragment mit dem Layout zum übergeordneten Layout hinzu. Wenn wir attachToRoot auf true setzen, erhalten Sie IllegalStateException: Das angegebene Kind hat bereits ein Elternteil. Da das Fragmentlayout bereits in der add () -Methode zum übergeordneten Layout hinzugefügt wurde.
Sie sollten für attachToRoot immer false übergeben, wenn Sie Fragmente aufblasen. Es ist Aufgabe des FragmentManagers, Fragmente hinzuzufügen, zu entfernen und zu ersetzen.
Zurück zu meinem Beispiel. Was ist, wenn wir beides tun?
In der ersten Zeile hängt LayoutInflater das Schaltflächenlayout an das Stammlayout an und gibt ein View-Objekt zurück, das dasselbe Schaltflächenlayout enthält. In der zweiten Zeile fügen wir dem übergeordneten Stammlayout dasselbe View-Objekt hinzu. Dies führt zu derselben IllegalStateException, die wir bei Fragmenten gesehen haben (das angegebene Kind hat bereits ein Elternteil).
Beachten Sie, dass es eine andere überladene inflate () -Methode gibt, mit der attachToRoot standardmäßig auf true gesetzt wird.
quelle
Aufgrund der Dokumentation zur inflate () -Methode gibt es zu diesem Thema viel Verwirrung.
Wenn attachToRoot auf true gesetzt ist, wird die im ersten Parameter angegebene Layoutdatei im Allgemeinen aufgeblasen und zu diesem Zeitpunkt an die im zweiten Parameter angegebene ViewGroup angehängt. Wenn attachToRoot false ist, wird die Layoutdatei aus dem ersten Parameter aufgeblasen und als Ansicht zurückgegeben, und jeder Ansichtsanhang erfolgt zu einem anderen Zeitpunkt.
Dies bedeutet wahrscheinlich nicht viel, es sei denn, Sie sehen viele Beispiele. Wenn Sie LayoutInflater.inflate () innerhalb der onCreateView-Methode eines Fragments aufrufen, möchten Sie false für attachToRoot übergeben, da die diesem Fragment zugeordnete Aktivität tatsächlich für das Hinzufügen der Ansicht dieses Fragments verantwortlich ist. Wenn Sie eine Ansicht zu einem späteren Zeitpunkt manuell aufblasen und einer anderen Ansicht hinzufügen, z. B. mit der Methode addView (), sollten Sie false für attachToRoot übergeben, da der Anhang zu einem späteren Zeitpunkt erstellt wird.
In einem Blog-Beitrag, den ich zu diesem Thema geschrieben habe, können Sie einige andere einzigartige Beispiele zu Dialogen und benutzerdefinierten Ansichten lesen.
https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
quelle
attachToRoot
Auf true gesetzt bedeutet, dass dasinflatedView
der Hierarchie der übergeordneten Ansicht hinzugefügt wird. Somit können Benutzer möglicherweise "gesehen" werden und Berührungsereignisse (oder andere UI-Operationen) erfassen. Andernfalls wurde es nur erstellt, keiner Ansichtshierarchie hinzugefügt und kann daher keine Berührungsereignisse sehen oder verarbeiten.Für iOS-Entwickler, die Android noch nicht kennen,
attachToRoot
bedeutet true, dass Sie diese Methode aufrufen:Wenn Sie weiter gehen, fragen Sie sich möglicherweise: Warum sollte ich die übergeordnete Ansicht übergeben, wenn ich auf eingestellt
attachToRoot
habefalse
? Dies liegt daran, dass das Stammelement in Ihrem XML-Baum die übergeordnete Ansicht benötigt, um einige LayoutParams zu berechnen (z. B. Match Parent).quelle
Wenn Sie das übergeordnete Element definieren, bestimmt attachToRoot, ob der Inflater es tatsächlich an das übergeordnete Element anhängen soll oder nicht. In einigen Fällen verursacht dies Probleme, wie in einem ListAdapter, der eine Ausnahme verursacht, da die Liste versucht, die Ansicht zur Liste hinzuzufügen, aber angibt, dass sie bereits angehängt ist. In anderen Fällen, in denen Sie die Ansicht nur selbst aufblasen, um sie zu einer Aktivität hinzuzufügen, kann dies nützlich sein und Ihnen eine Codezeile ersparen.
quelle
Zum Beispiel haben wir ein
ImageView
, einLinearLayout
und einRelativeLayout
. LinearLayout ist das untergeordnete Element von RelativeLayout. Die Ansichtshierarchie wird sein.und wir haben eine separate Layoutdatei für ImageView
image_view_layout.xml
An root anhängen:
setImageResource(R.drawable.np);
in ImageView festlegen möchten, müssen Sie sie anhand der Referenz des übergeordneten Layouts finden, d. H.view.findById()
Nicht an root anhängen:
view.setImageResource(R.drawable.np);
ohne Referenzierung wie einstellen könnenfindViewById
. Der Container wird jedoch so angegeben, dass ImageView die LayoutParams des Containers erhält, sodass Sie sagen können, dass die Referenz des Containers nur für LayoutParams bestimmt ist.quelle
attachToRoot Auf true setzen:
Stellen Sie sich vor, wir haben eine Schaltfläche in einer XML-Layoutdatei angegeben, deren Layoutbreite und Layouthöhe auf match_parent festgelegt sind.
Wir möchten diese Schaltfläche nun programmgesteuert zu einem linearen Layout innerhalb eines Fragments oder einer Aktivität hinzufügen. Wenn unser LinearLayout bereits eine Mitgliedsvariable, mLinearLayout, ist, können wir die Schaltfläche einfach wie folgt hinzufügen:
Wir haben angegeben, dass der Button aus seiner Layoutressourcendatei aufgeblasen werden soll. Anschließend teilen wir dem LayoutInflater mit, dass wir es an mLinearLayout anhängen möchten. Unsere Layoutparameter werden berücksichtigt, da wir wissen, dass der Button einem LinearLayout hinzugefügt wird. Der Layoutparametertyp der Schaltfläche sollte LinearLayout.LayoutParams sein.
attachToRoot Auf false setzen (nicht erforderlich, um false zu verwenden)
Lassen Sie uns einen Blick darauf werfen, wann Sie attachToRoot auf false setzen möchten. In diesem Szenario wird die im ersten Parameter von inflate () angegebene Ansicht zu diesem Zeitpunkt nicht an die ViewGroup im zweiten Parameter angehängt.
Erinnern Sie sich an unser Button-Beispiel von früher, in dem wir einen benutzerdefinierten Button aus einer Layoutdatei an mLinearLayout anhängen möchten. Wir können unseren Button weiterhin an mLinearLayout anhängen, indem wir false für attachToRoot übergeben - wir fügen ihn anschließend einfach manuell hinzu.
Diese beiden Codezeilen entsprechen dem, was wir zuvor in einer Codezeile geschrieben haben, als wir true für attachToRoot übergeben haben. Durch die Übergabe von false sagen wir, dass wir unsere Ansicht noch nicht an die Root-ViewGroup anhängen möchten. Wir sagen, dass es zu einem anderen Zeitpunkt passieren wird. In diesem Beispiel ist der andere Zeitpunkt einfach die addView () -Methode, die unmittelbar unterhalb der Inflation verwendet wird.
Das Beispiel "false attachToRoot" erfordert etwas mehr Arbeit, wenn wir die Ansicht manuell zu einer ViewGroup hinzufügen.
attachToRoot Auf false setzen (false ist erforderlich)
Wenn Sie die Ansicht eines Fragments in onCreateView () aufblasen und zurückgeben, müssen Sie false für attachToRoot übergeben. Wenn Sie true übergeben, erhalten Sie eine IllegalStateException, da das angegebene Kind bereits ein Elternteil hat. Sie sollten angegeben haben, wo die Ansicht Ihres Fragments wieder in Ihrer Aktivität platziert werden soll. Es ist Aufgabe des FragmentManagers, Fragmente hinzuzufügen, zu entfernen und zu ersetzen.
Der root_viewGroup-Container, der Ihr Fragment in Ihrer Aktivität enthält, ist der ViewGroup-Parameter, den Sie in onCreateView () in Ihrem Fragment erhalten haben. Es ist auch die ViewGroup, die Sie an LayoutInflater.inflate () übergeben. Der FragmentManager übernimmt jedoch das Anhängen der Ansicht Ihres Fragments an diese ViewGroup. Sie möchten es nicht zweimal anhängen. Setzen Sie attachToRoot auf false.
Warum erhalten wir die übergeordnete ViewGroup unseres Fragments überhaupt, wenn wir sie nicht in onCreateView () anhängen möchten? Warum fordert die inflate () -Methode eine Root-ViewGroup an?
Es stellt sich heraus, dass wir auch dann, wenn wir unsere neu aufgeblasene Ansicht nicht sofort zur übergeordneten ViewGroup hinzufügen, die LayoutParams der übergeordneten Ansicht verwenden sollten, damit die neue Ansicht ihre Größe und Position bestimmen kann, wenn sie schließlich angehängt wird.
Link: https://youtu.be/1Y0LlmTCOkM?t=409
quelle
Ich teile nur einige Punkte, die mir bei der Arbeit an diesem Thema begegnet sind.
Zusätzlich zu der akzeptierten Antwort möchte ich einige Punkte ansprechen, die hilfreich sein könnten.
Wenn ich attachToRoot als true verwendet habe, war die zurückgegebene Ansicht vom Typ ViewGroup, dh die Stammansicht ViewGroup des Elternteils, die als Parameter für die Inflate- Methode (layoutResource, ViewGroup, attachToRoot) übergeben wurde , nicht vom Typ des übergebenen Layouts, sondern von attachToRoot Als false erhalten wir den Funktionsrückgabetyp der Root- ViewGroup dieser layoutResource .
Lassen Sie mich anhand eines Beispiels erklären:
Wenn wir eine Linearlayout als Root - Layout und dann wollen wir hinzufügen Textview in ihm durch aufblasen Funktion.
Wenn Sie dann attachToRoot als echte Aufblasfunktion verwenden, wird eine Ansicht vom Typ LinearLayout zurückgegeben
während auf der Verwendung attachToRoot als falsch aufblasen Funktion gibt eine Ansicht vom Typ Textview
Hoffe, dieser Befund wäre hilfreich ...
quelle