wake-up-neo.com

Was bedeutet der Parameter LayoutInflater attachToRoot?

Das LayoutInflater.inflate Die Dokumentation über den Zweck des Parameters attachToRoot ist mir nicht genau klar.

attachToRoot : Soll die aufgeblähte Hierarchie an den root-Parameter angehängt werden? Wenn false, wird root nur verwendet, um die richtige Unterklasse von LayoutParams für die Stammansicht in der XML zu erstellen.

Könnte jemand bitte genauer erklären, was die Stammansicht ist, und vielleicht ein Beispiel für eine Änderung des Verhaltens zwischen true und false Werten zeigen?

160
Jeff Axelrod

Wenn der Wert auf true gesetzt ist, wird das aufgeblasene Layout automatisch der Ansichtshierarchie der ViewGroup hinzugefügt, die im 2. Parameter als untergeordnetes Element angegeben ist. Wenn der Stammparameter beispielsweise LinearLayout war, 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 (damit es nicht gezeichnet wird, keine Berührungsereignisse empfängt usw.).

91
Joseph Earl

JETZT OR NICHT JETZT

Der Hauptunterschied zwischen dem "dritten" Parameter attachToRoot, der wahr oder falsch ist, ist dies.

Wenn Sie attachToRoot setzen

true: füge die untergeordnete Ansicht zum übergeordneten Element hinzu RIGHT NOW
false: die untergeordnete Ansicht zum übergeordneten Element hinzufügen NOT NOW.
Füge es später hinzu. `

Wann ist das später?

Das ist später, wenn Sie zB für parent.addView(childView)

Ein häufiges Missverständnis Wenn der Parameter attachToRoot false ist, wird die untergeordnete Ansicht nicht zum übergeordneten Element hinzugefügt. FALSCH
In beiden Fällen wird die untergeordnete Ansicht zu parentView hinzugefügt. Es ist nur eine Frage von Zeit.

inflater.inflate(child,parent,false);
parent.addView(child);   

entspricht

inflater.inflate(child,parent,true);

A BIG NO-NO
Sie sollten attachToRoot niemals als true übergeben, wenn Sie nicht für das Hinzufügen der untergeordneten Ansicht zum übergeordneten Element verantwortlich sind.
ZB beim Hinzufügen von Fragment

public View onCreateView(LayoutInflater inflater,ViewGroup parent,Bundle bundle)
  {
        super.onCreateView(inflater,parent,bundle);
        View view = inflater.inflate(R.layout.image_fragment,parent,false);
        .....
        return view;
  }

wenn Sie den dritten Parameter als "true" übergeben, wird wegen dieses Typen eine "IllegalStateException" ausgegeben.

getSupportFragmentManager()
      .beginTransaction()
      .add(parent, childFragment)
      .commit();

Da Sie bereits aus Versehen das untergeordnete Fragment in onCreateView () hinzugefügt haben. Wenn Sie add aufrufen, wird angezeigt, dass die untergeordnete Ansicht bereits dem übergeordneten Element hinzugefügt wurde. Daher IllegalStateException.
Hier sind Sie nicht für das Hinzufügen von childView verantwortlich, FragmentManager ist dafür verantwortlich. Also in diesem Fall immer false übergeben.

HINWEIS: Ich habe auch gelesen, dass parentView keine childView touchEvents erhält, wenn attachToRoot false ist. Aber ich habe es nicht getestet.

87
Rohit Singh

Scheint viel Text in den Antworten zu enthalten, aber keinen Code. Deshalb habe ich mich entschlossen, diese alte Frage mit einem Codebeispiel wiederzubeleben. In mehreren Antworten wurde Folgendes erwähnt:

Wenn der Wert auf true gesetzt ist, wird das aufgeblasene Layout automatisch der Ansichtshierarchie der ViewGroup hinzugefügt, die im 2. Parameter als untergeordnetes Element angegeben ist.

Was das eigentlich im Code bedeutet (was die meisten Programmierer verstehen) ist:

public class MyCustomLayout extends LinearLayout {
    public MyCustomLayout(Context context) {
        super(context);
        // Inflate the view from the layout resource and pass it as child of mine (Notice I'm a LinearLayout class).

        LayoutInflater.from(context).inflate(R.layout.child_view, this, true);
    }
}

Beachten Sie, dass der vorherige Code das Layout R.layout.child_view Als untergeordnetes Element von MyCustomLayout hinzufügt, da attachToRoot param true ist, und die Layoutparameter des übergeordneten Elements genau in der genauso, als würde ich addView programmgesteuert verwenden, oder als würde ich dies in xml tun:

<LinearLayout>
   <View.../>
   ...
</LinearLayout>

Der folgende Code erklärt das Szenario, wenn attachRoot als false übergeben wird:

LinearLayout linearLayout = new LinearLayout(context);
linearLayout.setLayoutParams(new LayoutParams(
    LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
    // Create a stand-alone view
View myView = LayoutInflater.from(context)
    .inflate(R.layout.ownRootView, null, false);
linearLayout.addView(myView);

Im vorherigen Code haben Sie angegeben, dass myView ein eigenes Stammobjekt sein soll und nicht an ein übergeordnetes Objekt angehängt werden soll. Später haben wir es als Teil von LinearLayout hinzugefügt, aber für einen Moment war eine eigenständige Ansicht (keine übergeordnete Ansicht).

Das Gleiche passiert mit Fragmenten. Sie können sie zu einer bereits vorhandenen Gruppe hinzufügen und ein Teil davon sein oder einfach die Parameter übergeben:

inflater.inflate (R.layout.fragment, null, false);

Um anzugeben, dass es sich um eine eigene Root handelt.

36
Martin Cazares

Die Dokumentation und die beiden vorherigen Antworten sollten ausreichen, nur ein paar Gedanken von mir.

Die Methode inflate wird zum Aufblasen von Layoutdateien verwendet. Mit diesen aufgeblasenen Layouts müssen Sie die Möglichkeit haben, sie direkt an ein übergeordnetes ViewGroup anzuhängen oder einfach die Ansichtshierarchie aus dieser Layoutdatei aufzublasen und außerhalb der normalen Ansichtshierarchie damit zu arbeiten.

Im ersten Fall muss der Parameter attachToRoot auf true gesetzt werden (oder verwenden Sie einfach die Methode inflate, die eine Layoutdatei und ein übergeordnetes Stammverzeichnis ViewGroup (non null)). In diesem Fall ist das zurückgegebene View einfach das ViewGroup, das in der Methode übergeben wurde, das ViewGroup, zu dem die aufgeblasene Ansichtshierarchie hinzugefügt wird.

Bei der zweiten Option ist das zurückgegebene View das Stammverzeichnis ViewGroup aus der Layoutdatei. Wenn Sie sich an unsere letzte Diskussion aus der include-merge - Paarfrage erinnern, ist dies einer der Gründe für die Einschränkung von merge (wann) Wenn eine Layoutdatei mit merge als Root aufgeblasen ist, müssen Sie ein übergeordnetes Element angeben und attachedToRoot muss auf true) gesetzt sein. Wenn Sie eine Layoutdatei mit dem Root-Tag merge und attachedToRoot auf false gesetzt haben, muss die Methode inflate nichts zurückgeben, da merge dies nicht tut habe kein Äquivalent. Wie aus der Dokumentation hervorgeht, ist auch die inflate -Version mit attachToRoot auf false wichtig, da Sie die Ansichtshierarchie mit dem richtigen LayoutParams aus dem übergeordneten Element erstellen können. Dies ist in einigen Fällen wichtig, insbesondere bei den untergeordneten Elementen von AdapterView, einer Unterklasse von ViewGroup, für die die Methode addView() nicht unterstützt wird. Ich bin sicher, dass Sie sich erinnern, diese Zeile in der getView() -Methode verwendet zu haben:

convertView = inflater.inflate(R.layout.row_layout, parent, false);

Diese Zeile stellt sicher, dass die aufgeblasene R.layout.row_layout - Datei die richtige LayoutParams aus der AdapterView -Unterklasse hat, die in ihrem Stammverzeichnis ViewGroup festgelegt ist. Wenn Sie dies nicht tun würden, könnten Sie einige Probleme mit der Layoutdatei haben, wenn der Stamm ein RelativeLayout wäre. Die TableLayout/TableRow Haben auch einige spezielle und wichtige LayoutParams und Sie sollten sicherstellen, dass die Ansichten in ihnen die richtigen LayoutParams haben.

26
Luksprog

Ich selbst war auch verwirrt darüber, was der wahre Zweck von attachToRoot in inflate Methode war. Nach ein wenig 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 übergeordneten Elemente 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 übergeordneten Elemente, diese Ereignisse zu unterhalten oder zu ignorieren . Wenn auf false gesetzt, 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

17
Umer Farooq

Aufgrund der Dokumentation für die inflate () -Methode gibt es zu diesem Thema eine Menge Verwirrung.

Wenn attachToRoot auf true festgelegt 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 View zurückgegeben, und jeder View-Anhang wird zu einem anderen Zeitpunkt erstellt.

Das 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 mit diesem Fragment verknüpfte 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 (), möchten Sie für attachToRoot false übergeben, da die Anlage zu einem späteren Zeitpunkt erstellt wird.

In einem Blogbeitrag, den ich zu genau diesem Thema verfasst habe, finden Sie weitere einzigartige Beispiele für Dialoge und benutzerdefinierte Ansichten.

https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/

9
seanjfarrell

Ich habe diese Antwort geschrieben, weil ich selbst nach dem Durchlaufen mehrerer StackOverflow-Seiten nicht klar erfassen konnte, was attachToRoot bedeutet. Unten sehen Sie die inflate () - Methode in der LayoutInflater-Klasse.

View inflate (int resource, ViewGroup root, boolean attachToRoot)

Schauen Sie sich die Datei activity_main.xml , das Layout button.xml und an Die MainActivity.Java -Datei, die ich erstellt habe.

activity_main.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/root"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical">

</LinearLayout>

button.xml

<Button xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content" />

MainActivity.Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    LayoutInflater inflater = getLayoutInflater();
    LinearLayout root = (LinearLayout) findViewById(R.id.root);
    View view = inflater.inflate(R.layout.button, root, false);
}

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 festgelegt ist.

LinearLayout verfügt über eine Methode addView (View view) , mit der Sie Views zu LinearLayout hinzufügen können. Dadurch wird das Schaltflächenlayout dem Hauptaktivitätslayout hinzugefügt und die Schaltfläche wird sichtbar, wenn Sie den Code ausführen.

root.addView(view);

Entfernen wir die vorherige Zeile und sehen, was passiert, wenn wir attachToRoot auf true setzen.

View view = inflater.inflate(R.layout.button, root, true);

Wieder sehen wir, dass das Schaltflächenlayout sichtbar ist. Dies liegt daran, dass attachToRoot das aufgeblasene Layout direkt an das angegebene übergeordnete Element anfügt. Dies ist in diesem Fall root LinearLayout. Hier müssen wir die Ansichten nicht wie im vorherigen Fall mit der Methode addView (Ansichtsansicht) manuell hinzufügen.

Warum wird IllegalStateException angezeigt, wenn attachToRoot für ein Fragment als true festgelegt wird?

Dies liegt daran, dass Sie für ein Fragment bereits festgelegt haben, wo das Fragmentlayout in Ihrer Aktivitätsdatei platziert werden soll.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .add(R.id.root, fragment)
    .commit();

Mit dem Befehl add (int parent, Fragment fragment) wird das Fragment mit dem zugehörigen Layout zum übergeordneten Layout hinzugefügt. Wenn wir attachToRoot auf true setzen, erhalten Sie die IllegalStateException: Das angegebene untergeordnete Element hat bereits ein übergeordnetes Element. Da das Fragment-Layout 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 die Aufgabe des FragmentManagers, Fragmente hinzuzufügen, zu entfernen und zu ersetzen.

Zurück zu meinem Beispiel. Was ist, wenn wir beide machen?.

View view = inflater.inflate(R.layout.button, root, true);
root.addView(view);

In der ersten Zeile fügt LayoutInflater das Schaltflächenlayout dem Stammlayout hinzu und gibt ein View-Objekt zurück, das dasselbe Schaltflächenlayout enthält. In der zweiten Zeile fügen wir dasselbe View-Objekt zum übergeordneten Stammlayout hinzu. Dies führt zu der gleichen IllegalStateException wie bei Fragments (das angegebene Kind hat bereits ein Elternteil).

Beachten Sie, dass es eine weitere überladene inflate () -Methode gibt, die attachToRoot standardmäßig als true festlegt.

View inflate (int resource, ViewGroup root)
7
capt.swag

attachToRoot auf true gesetzt bedeutet, dass inflatedView der Hierarchie der übergeordneten Ansicht hinzugefügt wird. Auf diese Weise können Benutzer möglicherweise Berührungsereignisse (oder andere Benutzerschnittstellenoperationen) "sehen" und erfassen. Andernfalls wird 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, bedeutet attachToRoot true, dass Sie diese Methode aufrufen:

[parent addSubview:inflatedView];

Wenn Sie weiter gehen, könnten Sie fragen: Warum sollte ich die übergeordnete Ansicht übergeben, wenn ich attachToRoot auf false setze? Dies liegt daran, dass das Stammelement in Ihrem XML-Baum die übergeordnete Ansicht benötigt, um einige LayoutParams zu berechnen (z. B. übereinstimmende übergeordnete Elemente).

4
Alston

attachToRoot Auf true setzen:

Wenn attachToRoot auf true gesetzt ist, wird die im ersten Parameter angegebene Layoutdatei aufgeblasen und an die im zweiten Parameter angegebene ViewGroup angehängt.

Angenommen, wir haben eine Schaltfläche in einer XML-Layoutdatei angegeben, deren Layoutbreite und Layouthöhe auf match_parent festgelegt ist.

<Button xmlns:Android="http://schemas.Android.com/apk/res/Android"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:id="@+id/custom_button">
</Button>

Jetzt möchten wir diese Schaltfläche programmgesteuert einem LinearLayout 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:

inflater.inflate(R.layout.custom_button, mLinearLayout, true);

Wir haben angegeben, dass der Button aus seiner Layout-Ressourcendatei aufgeblasen werden soll. Dann 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 Layout-Parametertyp der Schaltfläche sollte LinearLayout.LayoutParams sein.

attachToRoot Auf false setzen (nicht erforderlich, um false zu verwenden)

Wenn attachToRoot auf false gesetzt ist, wird die im ersten Parameter angegebene Layoutdatei aufgeblasen und nicht an die im zweiten Parameter angegebene ViewGroup angehängt, sondern diese aufgeblasen Die Ansicht ruft die LayoutParams der Eltern auf , sodass diese Ansicht korrekt in die Eltern passt.


Schauen wir uns an, wann Sie attachToRoot auf false setzen möchten. In diesem Szenario ist die im ersten Parameter von inflate () angegebene Ansicht zu diesem Zeitpunkt nicht mit der ViewGroup im zweiten Parameter verknüpft.

Erinnern Sie sich an unser früheres Button-Beispiel, 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 manuell hinzu.

Button button = (Button) inflater.inflate(R.layout.custom_button,    mLinearLayout, false);
mLinearLayout.addView(button);

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 Methode addView (), die unmittelbar unterhalb der Inflation verwendet wird.

Das falsche attachToRoot-Beispiel 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 für attachToRoot false übergeben. Wenn Sie true übergeben, erhalten Sie eine IllegalStateException, da das angegebene untergeordnete Element bereits ein übergeordnetes Element hat. Sie sollten angegeben haben, wo die Ansicht Ihres Fragments wieder in Ihrer Aktivität platziert wird. Es ist die Aufgabe von FragmentManager, Fragmente hinzuzufügen, zu entfernen und zu ersetzen.

FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment =  fragmentManager.findFragmentById(R.id.root_viewGroup);

if (fragment == null) {
fragment = new MainFragment();
fragmentManager.beginTransaction()
    .add(R.id.root_viewGroup, fragment)
    .commit();
}

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

public View onCreateView(LayoutInflater inflater, ViewGroup  parentViewGroup, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout,     parentViewGroup, false);
…
return view;
}

Warum erhalten wir die übergeordnete ViewGroup unseres Fragments, 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 der übergeordneten ViewGroup hinzufügen, die LayoutParams der übergeordneten Ansicht verwenden sollten, damit die neue Ansicht ihre Größe und Position bestimmt, wenn sie schließlich angehängt wird.

Link: https://youtu.be/1Y0LlmTCOkM?t=409

0
Utshaw

Wenn Sie das übergeordnete Element definieren, bestimmt attachToRoot, ob der Inflater das Element tatsächlich an das übergeordnete Element anhängen soll oder nicht. In einigen Fällen führt dies zu Problemen, wie in einem ListAdapter, zu einer Ausnahme, da die Liste versucht, die Ansicht zur Liste hinzuzufügen, sie jedoch als bereits angefügt bezeichnet wird. In anderen Fällen, in denen Sie die Ansicht nur selbst vergrößern, um sie zu einer Aktivität hinzuzufügen, kann dies nützlich sein und Ihnen eine Codezeile ersparen.

0
CaseyB

Zum Beispiel haben wir ein ImageView, ein LinearLayout und ein RelativeLayout. LinearLayout ist das untergeordnete Element von RelativeLayout. Die Ansichtshierarchie lautet.

RelativeLayout
           ------->LinearLayout

und wir haben eine separate Layoutdatei für ImageView

image_view_layout.xml

An root anhängen:

//here container is the LinearLayout

    View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
  1. Hier enthält v die Referenz des Container-Layouts, d. H. Das LinearLayout. Wenn Sie die Parameter wie setImageResource(R.drawable.np); von ImageView festlegen möchten, müssen Sie sie anhand der Referenz des übergeordneten Elements finden, d. H. view.findById().
  2. Übergeordnetes Element von v ist das FrameLayout.
  3. LayoutParams bestehen aus FrameLayout.

Nicht an root anhängen:

//here container is the LinearLayout
    View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
  1. Hier enthält v kein Referenzcontainerlayout, sondern einen direkten Verweis auf die aufgeblasene ImageView, sodass Sie ihre Parameter wie view.setImageResource(R.drawable.np); festlegen können, ohne wie findViewById zu referenzieren. Der Container wird jedoch so angegeben, dass ImageView die LayoutParams des Containers abruft, sodass Sie sagen können, dass die Referenz des Containers nur für LayoutParams bestimmt ist.
  2. daher ist Parent im Einzelfall null.
  3. LayoutParams werden von LinearLayout sein.
0
Faisal Naseer