Verwendung von forceLayout (), requestLayout () und invalidate ()

184

Ich bin ein wenig verwirrt über die Rolle von forceLayout(), requestLayout()und invalidate()Methoden der ViewKlasse.

Wann sollen sie gerufen werden?

sdabet
quelle

Antworten:

356

Um die Antworten von François BOURLIEUX und Dalvik besser zu verstehen, empfehlen wir Ihnen, sich dieses beeindruckende Lebenszyklusdiagramm von Arpit Mathur anzusehen : Geben Sie hier die Bildbeschreibung ein

Bartek Lipinski
quelle
28
Ich sehe oft, dass requestLayout direkt nach dem Aufruf von invalidate aufgerufen wird. Ich sehe sogar, dass dies im Android-Quellcode für Dinge wie TextView geschieht, aber nach diesem Diagramm ist dies redundant, oder? Gibt es dafür einen Zweck?
tcox
5
Nun, das ist eine interessante Frage, und um ehrlich zu sein, weiß ich nicht wirklich, warum sie beide Methoden in z TextView. Ich dachte, dass sie das vielleicht Viewzum letzten Mal zeichnen möchten, bevor sie ihre layoutbezogenen Parameter ändern, aber es macht keinen Sinn, wenn wir darüber nachdenken, dass diese Aufrufe in der anderen Reihenfolge aufgerufen werden (und sie rufen invalidate()direkt nach requestLayout()in auf TextViewauch). Vielleicht verdient es eine andere Frage zu StackOverflow :)?
Bartek Lipinski
14
(1/2) : Ich glaube nicht, dass Sie diese beiden Methoden ( invalidate()und requestLayout()) richtig verstehen . Der Zweck dieser Methoden besteht darin, festzustellen, Viewwelche Art von Ungültigmachung (wie Sie sie genannt haben) stattgefunden hat. Es ist nicht so, als würde man Viewentscheiden, welchem ​​Pfad man folgen soll, nachdem man eine dieser Methoden aufgerufen hat. Die Logik hinter der Auswahl des View-lifecycle-Pfades ist die Auswahl der richtigen Methode, um sich selbst aufzurufen. Wenn es etwas im Zusammenhang mit Größenänderungen gibt - requestLayout()sollte aufgerufen werden, wenn es nur eine visuelle Änderung ohne Größenänderung gibt - sollten Sie anrufen invalidate().
Bartek Lipinski
11
(2/2): Wenn Sie die Größe Ihrer ändern Viewin irgendeiner Weise, zB Sie erhalten StromLayoutParams von ein Viewund Sie ändern sie, aber nicht nennen entweder requestLayoutoder setLayoutParams(welche Anrufe requestLayoutintern), dann können Sie anrufen , invalidate()so viel wie Sie wollen, und Das Viewwird den Measure-Layout-Prozess nicht durchlaufen und daher seine Größe nicht ändern. Wenn Sie Ihrem nicht mitteilen, Viewdass sich seine Größe geändert hat (mit einem requestLayoutMethodenaufruf), Viewwird davon ausgegangen, dass dies nicht der Fall ist , und das onMeasureund onLayoutwird nicht aufgerufen.
Bartek Lipinski
2
@tcox mehr hier -> stackoverflow.com/questions/35279374/…
Sotti
124

invalidate()

Der Anruf invalidate()wird ausgeführt, wenn Sie ein erneutes Zeichnen der Ansicht planen möchten. Es wird dazu führen, onDrawdass es irgendwann angerufen wird (bald, aber nicht sofort). Ein Beispiel dafür, wann eine benutzerdefinierte Ansicht sie aufrufen würde, ist, wenn sich eine Text- oder Hintergrundfarbeneigenschaft geändert hat.

Die Ansicht wird neu gezeichnet, die Größe ändert sich jedoch nicht.

requestLayout()

Wenn sich etwas an Ihrer Ansicht ändert, das sich auf die Größe auswirkt, sollten Sie anrufen requestLayout(). Dies löst onMeasureund onLayoutnicht nur für diese Ansicht aber den ganzen Weg bis die Leitung für die übergeordneten Ansichten.

Es requestLayout()wird nicht garantiert, dass einonDraw Anruf zu einem führt (im Gegensatz zu dem, was das Diagramm in der akzeptierten Antwort impliziert), daher wird es normalerweise mit kombiniert invalidate().

invalidate();
requestLayout();

Ein Beispiel hierfür ist, wenn die Texteigenschaft einer benutzerdefinierten Beschriftung geändert wird. Das Etikett würde seine Größe ändern und muss daher neu gemessen und neu gezeichnet werden.

forceLayout()

Wenn requestLayout()eine übergeordnete Ansichtsgruppe aufgerufen wird, muss die untergeordnete Ansicht nicht erneut gemessen und weitergeleitet werden. Wenn jedoch ein Kind in die erneute Messung und Weiterleitung einbezogen werden soll, können Sie forceLayout()das Kind anrufen . forceLayout()Funktioniert nur bei einem Kind, wenn es in Verbindung mit einem requestLayout()bei seinem direkten Elternteil auftritt . Das Aufrufen forceLayout()von sich selbst hat keine Auswirkung, da requestLayout()der Ansichtsbaum nicht nach oben ausgelöst wird .

Lesen Sie diese Fragen und Antworten für eine detailliertere Beschreibung von forceLayout().

Weitere Studie

Suragch
quelle
1
Aber wäre es dann nicht sinnvoller, zuerst requestLayout () aufzurufen und dann ungültig zu machen ()?
Scholt
2
@scholt, Soweit ich weiß, spielt die Reihenfolge keine Rolle, also kannst du requestLayout()vorher anrufen, invalidate()wenn du willst. Keiner macht ein Layout oder zeichnet sofort. Vielmehr setzen sie Flags, die schließlich zu einem Relayout und einem Neuzeichnen führen.
Suragch
27

Hier finden Sie eine Antwort: http://developer.android.com/guide/topics/ui/how-android-draws.html

Für mich invalidate()aktualisiert ein Aufruf, nur die Ansicht zu requestLayout()aktualisieren, und ein Aufruf, die Ansicht zu aktualisieren und die Größe der Ansicht auf dem Bildschirm zu berechnen.

François BOURLIEUX
quelle
3
Wie wäre es dann mit forceLayout ()?
Sdabet
@ Fiddler, diese Methode setzt nur zwei Flags: PFLAG_FORCE_LAYOUT und PFLAG_INVALIDATED
Suitianshi
7
@suitianshi Was sind die Konsequenzen, wenn die Flaggen gesetzt werden?
Sergey
1
@Sergey Die Konsequenz scheint zu sein, dass dieses Flag den Messcache überschreibt (Ansichten verwenden den Cache, um in Zukunft für dieselbe MeasureSpec schneller zu messen). siehe Zeile 18783 von View.java hier: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim
3

Wenn Sie invalidate () für eine Ansicht verwenden, die Sie neu zeichnen möchten, wird onDraw (Canvas c) aufgerufen, und requestLayout () lässt das gesamte Layout-Rendering (Messphase und Positionierungsphase) erneut ausgeführt werden. Sie sollten es verwenden, wenn Sie die Größe der untergeordneten Ansicht zur Laufzeit ändern, jedoch nur in bestimmten Fällen, z. B. Einschränkungen aus der übergeordneten Ansicht (damit meine ich, dass die übergeordnete Höhe oder Breite WRAP_CONTENT ist und daher die untergeordneten Elemente übereinstimmen, bevor sie sie erneut umbrechen können).

Nativ
quelle
3

Diese Antwort ist nicht richtig forceLayout().

Wie Sie im Code sehenforceLayout() können, markiert die Ansicht lediglich die Ansicht als "benötigt ein Relayout", aber sie plant oder löst dieses Relayout weder aus. Das Relayout wird erst zu einem späteren Zeitpunkt stattfinden, wenn das übergeordnete Element der Ansicht aus einem anderen Grund festgelegt wurde.

Es gibt auch ein viel größeres Problem bei der Verwendung von forceLayout()und requestLayout():

Angenommen, Sie haben forceLayout()eine Ansicht aufgerufen . Wenn Sie nun requestLayout()einen Nachkommen dieser Ansicht aufrufen, ruft Android rekursiv requestLayout()die Vorfahren dieses Nachkommen auf. Das Problem ist, dass die Rekursion bei der Ansicht, die Sie aufgerufen haben, gestoppt wird forceLayout(). Der requestLayout()Aufruf erreicht also niemals den Ansichtsstamm und plant daher niemals einen Layoutdurchlauf. Ein ganzer Teilbaum der Ansichtshierarchie wartet auf ein Layout, und das Aufrufen requestLayout()einer Ansicht dieses Teilbaums führt nicht zu einem Layout. Nur requestLayout()wenn Sie eine Ansicht außerhalb dieses Teilbaums aufrufen , wird der Zauber gebrochen.

Ich würde die Implementierung von forceLayout()(und wie es sich auswirkt requestLayout(), wenn sie kaputt geht, in Betracht ziehen und Sie sollten diese Funktion niemals in Ihrem Code verwenden.

Fluidsonic
quelle
1
Hallo! Wie bist du auf diesen Zauber gekommen ? Ist das irgendwo dokumentiert?
Azizbekian
1
Leider ist es nicht dokumentiert und wahrscheinlich nicht einmal beabsichtigt. Ich habe das herausgefunden, indem ich den Code von untersucht Viewund das Problem selbst festgestellt und es debuggt habe.
Fluidsonic
Dann bin ich neugierig auf den Zweck der forceLayout()API: Sie erzwingt keinen Layout-Pass, sondern ändert nur ein Flag, das in beobachtetonMeasure() wird, onMeasure()wird aber nur aufgerufen, wenn requestLayout()ein expliziter View#measure()Aufruf erfolgt. Das heißt, das forceLayout()sollte gepaart werden requestLayout(). Auf der anderen Seite, warum dann durchführen, forceLayout()wenn ich noch durchführen muss requestLayout()?
Azizbekian
@azizbekian nein, das musst du nicht. requestLayout()macht alles was forceLayout()auch macht.
Fluidsonic
1
@ Suragch hat das verpasst, danke! Sehr interessante Analyse. Der Verwendungszweck forceLayoutmacht Sinn. Am Ende ist es also nur sehr schlecht benannt und dokumentiert.
Fluidsonic
0

invalidate() ---> onDraw() vom UI-Thread

postInvalidate() ---> onDraw() aus dem Hintergrund-Thread

requestLayout()---> onMeasure()und onLayout()UND NICHT NOTWENDIG onDraw()

  • WICHTIG : Das Aufrufen dieser Methode wirkt sich nicht auf das untergeordnete Element der aufgerufenen Klasse aus.

forceLayout()---> onMeasure()und onLayout() NUR WENN der direkte Elternteil anruft requestLayout().

Ehsan Mashhadi
quelle