Użycie forceLayout (), requestLayout () i validate ()

185

Jestem nieco mylić o rolach forceLayout(), requestLayout()i invalidate()metod Viewklasy.

Kiedy oni zostaną wezwani?

sdabet
źródło

Odpowiedzi:

357

Aby lepiej zrozumieć odpowiedzi udzielone przez François BOURLIEUX i Dalvik , proponuję rzucić okiem na ten niesamowity schemat cyklu życia widoku autorstwa Arpit Mathur : wprowadź opis zdjęcia tutaj

Bartek Lipiński
źródło
28
Często widzę, że requestLayout jest wywoływany bezpośrednio po wywołaniu unieważnienia, nawet widzę, że dzieje się to w kodzie źródłowym Androida dla takich rzeczy jak TextView, ale zgodnie z tym diagramem jest to zbędne, prawda? Czy jest więc jakiś cel?
tcox
5
Cóż, to interesujące pytanie, i szczerze mówiąc, tak naprawdę nie wiem, dlaczego nazywają obie metody np TextView. Myślałem, że może chcą zwrócić Viewsię 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 TextViewtakże). Może zasługuje na inne pytanie na StackOverflow :)?
Bartek Lipiński
14
(1/2) : Nie sądzę, abyś dobrze zrozumiał te dwie metody ( invalidate()i requestLayout()). Celem tych metod jest poinformowanie, Viewjaki rodzaj unieważnienia (jak to nazwałeś) miał miejsce. To nie jest tak, że Viewdecyduje, którą ścieżką podążać po wywołaniu jednej z tych metod. ViewLogiką 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().
Bartek Lipiński
11
(2/2): Jeśli zmienisz Vieww jakiś sposób swój rozmiar , np. Uzyskasz prądLayoutParams a Viewi zmodyfikujesz je, ale NIE zadzwonisz albo, requestLayoutani setLayoutParams(który dzwoni requestLayoutwewnętrznie), możesz zadzwonić invalidate()tyle, ile chcesz, i Viewnie 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 requestLayoutwywołania metody), wtedy Viewbędzie zakładać, że nie, a onMeasurei onLayoutnie zostanie wywołana.
Bartek Lipiński
2
@tcox więcej tutaj -> stackoverflow.com/questions/35279374/…
Sotti
125

invalidate()

Wywoływanie invalidate()odbywa się, gdy chcesz zaplanować przerysowanie widoku. Spowoduje to, że onDrawzostanie 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 onLayoutnie 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().

Dalsze badanie

Suragch
źródło
1
ale czy nie miałoby wtedy większego sensu wywoływanie requestLayout (), a następnie validate ()?
scholt
2
@scholt, O ile wiem, kolejność nie ma znaczenia, więc możesz zadzwonić 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.
Suragch
27

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.

François BOURLIEUX
źródło
3
co powiesz na forceLayout ()?
sdabet
@ Fiddler, ta metoda ustawia tylko dwie flagi: PFLAG_FORCE_LAYOUT i PFLAG_INVALIDATED
suitianshi
7
@suitianshi Jakie są zatem konsekwencje ustawienia flag?
Siergiej
1
@Sergey Konsekwencją wydaje się być to, że ta flaga zastępuje pomiar pamięci podręcznej (widoki używają pamięci podręcznej, aby mierzyć szybciej w przyszłości dla tego samego parametru MeasureSpec); patrz wiersz 18783 View.java tutaj: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim
3

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ąć)

Nativ
źródło
3

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

fluidsonic
źródło
1
Cześć! Jak wymyśliłeś to zaklęcie ? Czy to gdzieś jest udokumentowane?
azizbekian
1
Niestety nie jest to udokumentowane i prawdopodobnie nawet nie zamierzone. Zorientowałem się, badając kod Viewi sam napotykając problem, i debugując go.
Fluidsonic
Ciekawi mnie zatem cel 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()?
azizbekian
@azizbekian nie, nie musisz. requestLayout()robi wszystko, co forceLayout()również.
Fluidsonic
1
@Suragch przegapił to, dzięki! Bardzo interesująca analiza. Zamierzone zastosowanie forceLayoutma sens. W końcu jest to po prostu bardzo źle nazwane i udokumentowane.
Fluidsonic
0

invalidate()---> onDraw()z wątku interfejsu użytkownika

postInvalidate()---> onDraw()z wątku w tle

requestLayout()---> onMeasure()i onLayout()I niekoniecznie onDraw()

  • WAŻNE : Wywołanie tej metody nie wpływa na dziecko klasy wywoływanej.

forceLayout()---> onMeasure()i onLayout() JEŚLI JEŚLI bezpośredni rodzic zadzwonił requestLayout().

Ehsan Mashhadi
źródło