Utilizzo di forceLayout (), requestLayout () e invalidate ()


185

Sono un po 'confuso circa i ruoli di forceLayout(), requestLayout()e invalidate()metodi della Viewclasse.

Quando devono essere chiamati?

Risposte:


357

Per comprendere meglio le risposte fornite da François BOURLIEUX e Dalvik, ti suggerisco di dare un'occhiata a questo fantastico diagramma del ciclo di vita di Arpit Mathur : inserisci qui la descrizione dell'immagine


28
Vedo spesso requestLayout che viene chiamato direttamente dopo che viene chiamato invalidate, vedo persino che succede nel codice sorgente Android per cose come TextView, ma secondo questo diagramma farlo è ridondante, giusto? Quindi c'è uno scopo per farlo?
tcox,

5
Bene, questa è una domanda interessante, e ad essere sincero, non so davvero perché chiamino entrambi i metodi, ad es TextView. Ho pensato che forse vogliono disegnare Viewper l'ultima volta prima di cambiare i parametri relativi al layout, ma non ha davvero senso se pensiamo a quelle chiamate invocate nel diverso ordine (e chiamano invalidate()subito dopo requestLayout()in TextViewanche). Forse merita un'altra domanda su StackOverflow :)?
Bartek Lipinski,

14
(1/2) : Non credo che tu capisca quei due metodi ( invalidate()e requestLayout()) correttamente. Lo scopo di questi metodi è di dire Viewche tipo di invalidazione (come l'hai chiamata tu) è avvenuta. Non è come Viewdecide quale percorso seguire dopo aver chiamato uno di questi metodi. La logica dietro la scelta del View-lifecycle-path è la scelta del metodo corretto per chiamare se stesso. Se c'è qualcosa legato alla modifica della dimensione - requestLayout()dovrebbe essere chiamato, se c'è solo una modifica visiva senza una modifica della dimensione - dovresti chiamare invalidate().
Bartek Lipinski,

11
(2/2): se modifichi Viewin qualche modo la dimensione del tuo , ad esempio ottieni correnteLayoutParams di un Viewe li modifichi, ma NON chiami uno requestLayouto setLayoutParams(che chiama requestLayoutinternamente), allora puoi chiamare invalidate()quanto vuoi, e il Viewnon passerà attraverso il processo di misura layout, quindi non cambierà la sua dimensione. Se non dici al tuo Viewche le sue dimensioni sono cambiate (con una requestLayoutchiamata al metodo), allora Viewsupporrà che non sia cambiato e onMeasuree onLayoutnon verrà chiamato.
Bartek Lipinski,


125

invalidate()

La chiamata invalidate()viene effettuata quando si desidera pianificare un ridisegno della vista. Alla fine verrà onDrawchiamato (presto, ma non immediatamente). Un esempio di quando una vista personalizzata lo chiamerebbe è quando una proprietà del colore del testo o dello sfondo è cambiata.

La vista verrà ridisegnata ma le dimensioni non cambieranno.

requestLayout()

Se cambia qualcosa nella tua vista che influirà sulla dimensione, dovresti chiamare requestLayout(). Questo attiverà onMeasuree onLayoutnon solo per questa vista ma fino in fondo per le viste principali.

NonrequestLayout() è garantito che laonDraw chiamata dia luogo a (contrariamente a quanto implica il diagramma nella risposta accettata), quindi di solito è combinata con invalidate().

invalidate();
requestLayout();

Un esempio di ciò è quando un'etichetta personalizzata ha la proprietà text modificata. L'etichetta cambierebbe dimensione e quindi dovrebbe essere rimisurata e ridisegnata.

forceLayout()

Quando è presente un oggetto requestLayout()chiamato in un gruppo di viste padre, non è necessario rimisurare e inoltrare le viste figlio. Tuttavia, se un bambino deve essere incluso nella rimisurazione e nel relè, è possibile chiamare forceLayout()il bambino. forceLayout()Funziona su un bambino solo se si verifica in congiunzione con un requestLayout()suo genitore diretto. La chiamata forceLayout()da sola non avrà alcun effetto poiché non attiva un requestLayout()albero di visualizzazione.

Leggi le domande e risposte per una descrizione più dettagliata di forceLayout().

Ulteriore studio


1
ma non avrebbe più senso chiamare prima requestLayout () e quindi invalidate ()?
scholt

2
@scholt, per quanto ne so l'ordine non importa, quindi puoi chiamare requestLayout()prima invalidate()se vuoi. Nessuno dei due fa un layout o disegna immediatamente. Piuttosto, impostano flag che alla fine si tradurranno in un relayout e ridisegna.
Suragch,

27

Qui puoi trovare qualche risposta: http://developer.android.com/guide/topics/ui/how-android-draws.html

Per me una chiamata invalidate()aggiorna solo la vista e una chiamata per requestLayout()aggiornare la vista e calcolare la dimensione della vista sullo schermo.


3
che ne dici di forceLayout () allora?
sdabet,

@fiddler, questo metodo imposta solo due flag: PFLAG_FORCE_LAYOUT e PFLAG_INVALIDATED
suitianshi

7
@suitianshi Quali sono le conseguenze quando si posizionano le bandiere?
Sergey,

1
@Sergey La conseguenza sembra essere che questo flag ignori la cache di misura (le viste usano la cache per misurare più velocemente in futuro per lo stesso MeasureSpec); vedere la riga 18783 di View.java qui: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim

3

usi invalidate () su una vista che vuoi ridisegnare, invocherà il suo onDraw (Canvas c) per invocare, e requestLayout () farà eseguire nuovamente il rendering dell'intero layout (fase di misurazione e fase di posizionamento). Dovresti usarlo se stai modificando le dimensioni della vista figlio in fase di esecuzione ma solo in casi particolari come vincoli dalla vista padre (intendo che l'altezza o la larghezza del padre sono WRAP_CONTENT e quindi abbina misura i bambini prima che possano avvolgerli di nuovo)


3

Questa risposta non è correttaforceLayout() .

Come puoi vedere nel codice diforceLayout() , la vista viene semplicemente contrassegnata come "necessita di un relayout" ma non pianifica né attiva quel relayout. Il relayout non avverrà fino a quando, in futuro, il genitore della view non sarà disposto per qualche altro motivo.

C'è anche un problema molto più grande quando si utilizza forceLayout()e requestLayout():

Supponiamo che tu abbia richiamato forceLayout()una vista. Ora quando si chiama requestLayout()un discendente di quella vista, Android chiamerà ricorsivamente requestLayout()gli antenati di quel discendente. Il problema è che interromperà la ricorsione nella vista su cui hai chiamato forceLayout(). Quindi la requestLayout()chiamata non raggiungerà mai la radice della vista e quindi non pianificherà mai un passaggio di layout. Un'intera sottostruttura della gerarchia della vista è in attesa di un layout e la chiamata requestLayout()a qualsiasi vista di tale sottostruttura non causerà un layout. Richiamare requestLayout()qualsiasi vista al di fuori di quella sottostruttura interromperà l'incantesimo.

Considererei l'implementazione di forceLayout()(e il modo in cui influisce requestLayout()per essere rotto e non dovresti mai usare quella funzione nel tuo codice.


1
Ciao! Come siete venuti con quella magia ? È documentato da qualche parte?
Azizbekian,

1
Purtroppo non è documentato e probabilmente non è nemmeno previsto. L'ho capito investigando il codice Viewe incappando nel problema da solo e eseguendo il debug.
fluidsonic,

Quindi sono curioso dello scopo forceLayout()dell'API: in realtà non impone un passaggio di layout, piuttosto cambia solo un flag che viene osservato inonMeasure() , ma onMeasure()non verrà chiamato a meno che non venga chiamato requestLayout()un esplicito View#measure(). Ciò significa che forceLayout()dovrebbe essere associato requestLayout(). D'altra parte, perché allora esibirmi forceLayout()se devo ancora esibirmi requestLayout()?
Azizbekian,

@azizbekian no, non è necessario. requestLayout()fa tutto ciò che forceLayout()fa anche.
fluidsonic,

1
@Suragch ha perso quello, grazie! Analisi molto interessanti. L'uso previsto di forceLayoutha senso. Quindi alla fine ha solo un nome e una documentazione pessimi.
fluidsonic,

0

invalidate() ---> onDraw() dal thread dell'interfaccia utente

postInvalidate() ---> onDraw() dal thread di sfondo

requestLayout()---> onMeasure()ed onLayout()E non necessariamente onDraw()

  • IMPORTANTE : la chiamata a questo metodo non influisce sul figlio della classe chiamata.

forceLayout()---> onMeasure()e onLayout() SOLO SE il genitore diretto ha chiamato requestLayout().

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.