Antworten:
Um die Antworten von François BOURLIEUX und Dalvik besser zu verstehen, empfehlen wir Ihnen, sich dieses beeindruckende Lebenszyklusdiagramm von Arpit Mathur anzusehen :
TextView
. Ich dachte, dass sie das vielleicht View
zum 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 TextView
auch). Vielleicht verdient es eine andere Frage zu StackOverflow :)?
invalidate()
und requestLayout()
) richtig verstehen . Der Zweck dieser Methoden besteht darin, festzustellen, View
welche Art von Ungültigmachung (wie Sie sie genannt haben) stattgefunden hat. Es ist nicht so, als würde man View
entscheiden, 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()
.
View
in irgendeiner Weise, zB Sie erhalten StromLayoutParams
von ein View
und Sie ändern sie, aber nicht nennen entweder requestLayout
oder setLayoutParams
(welche Anrufe requestLayout
intern), dann können Sie anrufen , invalidate()
so viel wie Sie wollen, und Das View
wird den Measure-Layout-Prozess nicht durchlaufen und daher seine Größe nicht ändern. Wenn Sie Ihrem nicht mitteilen, View
dass sich seine Größe geändert hat (mit einem requestLayout
Methodenaufruf), View
wird davon ausgegangen, dass dies nicht der Fall ist , und das onMeasure
und onLayout
wird nicht aufgerufen.
invalidate()
Der Anruf invalidate()
wird ausgeführt, wenn Sie ein erneutes Zeichnen der Ansicht planen möchten. Es wird dazu führen, onDraw
dass 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 onMeasure
und onLayout
nicht 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()
.
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.
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.
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).
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.
View
und das Problem selbst festgestellt und es debuggt habe.
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()
?
requestLayout()
macht alles was forceLayout()
auch macht.
forceLayout
macht Sinn. Am Ende ist es also nur sehr schlecht benannt und dokumentiert.
invalidate()
---> onDraw()
vom UI-Thread
postInvalidate()
---> onDraw()
aus dem Hintergrund-Thread
requestLayout()
---> onMeasure()
und onLayout()
UND NICHT NOTWENDIG onDraw()
forceLayout()
---> onMeasure()
und onLayout()
NUR WENN der direkte Elternteil anruft requestLayout()
.