Odpowiedzi:
Aby lepiej zrozumieć odpowiedzi udzielone przez François BOURLIEUX i Dalvik , proponuję rzucić okiem na ten niesamowity schemat cyklu życia widoku autorstwa Arpit Mathur :
TextView
. Myślałem, że może chcą zwrócić View
się po raz ostatni, zanim zmieni swoje parametry związane z układu, ale to nie ma żadnego sensu, jeśli myślimy o tych rozmów powołano w innej kolejności (i nazywają invalidate()
zaraz po requestLayout()
w TextView
także). Może zasługuje na inne pytanie na StackOverflow :)?
invalidate()
i requestLayout()
). Celem tych metod jest poinformowanie, View
jaki rodzaj unieważnienia (jak to nazwałeś) miał miejsce. To nie jest tak, że View
decyduje, którą ścieżką podążać po wywołaniu jednej z tych metod. View
Logiką wyboru ścieżki -lifecycle jest wybór właściwej metody, która ma się nazywać. Jeśli jest coś związanego ze zmianą rozmiaru - requestLayout()
należy nazywać, jeśli jest tylko zmiana wizualna bez zmiany rozmiaru - należy zadzwonić invalidate()
.
View
w jakiś sposób swój rozmiar , np. Uzyskasz prądLayoutParams
a View
i zmodyfikujesz je, ale NIE zadzwonisz albo, requestLayout
ani setLayoutParams
(który dzwoni requestLayout
wewnętrznie), możesz zadzwonić invalidate()
tyle, ile chcesz, i View
nie przejdzie przez proces miara DTP, zatem nie będzie zmienić jego rozmiar. Jeżeli nie mów View
, że jego wielkość uległa zmianie (z requestLayout
wywołania metody), wtedy View
będzie zakładać, że nie, a onMeasure
i onLayout
nie zostanie wywołana.
invalidate()
Wywoływanie invalidate()
odbywa się, gdy chcesz zaplanować przerysowanie widoku. Spowoduje to, że onDraw
zostanie ostatecznie wywołany (wkrótce, ale nie natychmiast). Przykładem wywołania widoku niestandardowego jest zmiana właściwości koloru tekstu lub tła.
Widok zostanie przerysowany, ale rozmiar się nie zmieni.
requestLayout()
Jeśli coś w twoim widoku zmieni się, co wpłynie na rozmiar, powinieneś zadzwonić requestLayout()
. Spowoduje to onMeasure
, a onLayout
nie tylko dla tego widoku, ale aż do góry linii do poglądów dominujących.
Wywołanie requestLayout()
jest nie gwarantują wyniku wonDraw
(w przeciwieństwie do tego, co schemat w przyjętym odpowiedź wskazuje), więc to jest zwykle połączone z invalidate()
.
invalidate();
requestLayout();
Przykładem tego jest zmiana właściwości tekstowej etykiety niestandardowej. Etykieta zmieniłaby rozmiar, w związku z czym należy ją poprawić i przerysować.
forceLayout()
Kiedy istnieje requestLayout()
nadrzędna grupa widoków, nie trzeba ponownie oceniać i przekazywać jej widoków potomnych. Jeśli jednak dziecko powinno zostać włączone do ponownej oceny i przekazania, możesz je wezwać forceLayout()
. forceLayout()
działa tylko na dziecko, jeśli występuje w połączeniu z requestLayout()
jego bezpośrednim rodzicem. Wywołanie forceLayout()
sam będzie miał żadnego wpływu, ponieważ nie wyzwolić requestLayout()
się widoku drzewa.
Przeczytaj te pytania i odpowiedzi, aby uzyskać bardziej szczegółowy opis forceLayout()
.
requestLayout()
wcześniej, invalidate()
jeśli chcesz. Żaden z nich nie tworzy układu ani nie rysuje natychmiast. Zamiast tego ustawiają flagi, które ostatecznie skutkują sztafetą i przerysowaniem.
Tutaj znajdziesz odpowiedź: http://developer.android.com/guide/topics/ui/how-android-draws.html
Dla mnie wezwanie do invalidate()
odświeżenia widoku i wezwanie do requestLayout()
odświeżenia widoku i obliczenia rozmiaru widoku na ekranie.
użyjesz validate () w widoku, który chcesz przerysować, spowoduje to wywołanie jego onDraw (Canvas c), a requestLayout () spowoduje ponowne uruchomienie renderowania całego układu (faza pomiaru i faza pozycjonowania). Powinieneś go użyć, jeśli zmieniasz rozmiar widoku potomnego w środowisku wykonawczym, ale tylko w szczególnych przypadkach, takich jak ograniczenia z widoku rodzica (rozumiem przez to, że wysokość lub szerokość rodzica są WRAP_CONTENT i dlatego dopasuj zmierz dzieci, zanim będą mogły je ponownie owinąć)
Ta odpowiedź jest nieprawidłowa forceLayout()
.
Jak widać w kodzieforceLayout()
, zaznacza on widok jako „potrzebuje przekaźnika”, ale nie planuje ani nie wyzwala tego przekaźnika. Przekazywanie nastąpi dopiero w pewnym momencie, w którym rodzic widoku został ustanowiony z innego powodu.
Istnieje również znacznie większy problem podczas używania forceLayout()
i requestLayout()
:
Powiedzmy, że wywołałeś forceLayout()
widok. Teraz, gdy wzywamy requestLayout()
potomka tego widoku, Android będzie rekurencyjnie wzywał requestLayout()
przodków tego potomka. Problem polega na tym, że zatrzyma rekurencję w widoku, do którego zadzwoniłeś forceLayout()
. Tak więc requestLayout()
połączenie nigdy nie osiągnie katalogu głównego widoku, a zatem nigdy nie planuje przejścia układu. Całe poddrzewo hierarchii widoku czeka na układ, a wywołanie requestLayout()
dowolnego widoku tego poddrzewa nie spowoduje układu. Tylko wywołanie requestLayout()
dowolnego widoku poza tym poddrzewem złamie czar.
Zastanowiłbym się nad implementacją forceLayout()
(i jak to wpływa requestLayout()
na uszkodzenie i nigdy nie powinieneś używać tej funkcji w swoim kodzie).
View
i sam napotykając problem, i debugując go.
forceLayout()
API: nie wymusza to przejścia przez układ, a jedynie zmienia flagę, która jest obserwowanaonMeasure()
, ale onMeasure()
nie zostanie wywołana, chyba że zostanie wywołane requestLayout()
jawne View#measure()
. Oznacza to, że forceLayout()
należy to połączyć requestLayout()
. Z drugiej strony, po co więc wykonywać, forceLayout()
jeśli nadal muszę występować requestLayout()
?
requestLayout()
robi wszystko, co forceLayout()
również.
forceLayout
ma sens. W końcu jest to po prostu bardzo źle nazwane i udokumentowane.
invalidate()
---> onDraw()
z wątku interfejsu użytkownika
postInvalidate()
---> onDraw()
z wątku w tle
requestLayout()
---> onMeasure()
i onLayout()
I niekoniecznie onDraw()
forceLayout()
---> onMeasure()
i onLayout()
JEŚLI JEŚLI bezpośredni rodzic zadzwonił requestLayout()
.