Uso de forceLayout (), requestLayout () e invalidate ()

185

Estou um pouco confuso sobre os papéis de forceLayout(), requestLayout()e invalidate()métodos da Viewclasse.

Quando eles serão chamados?

sdabet
fonte

Respostas:

357

Para entender melhor as respostas fornecidas por François BOURLIEUX e Dalvik , sugiro que você dê uma olhada neste incrível diagrama de ciclo de vida da vista por Arpit Mathur : insira a descrição da imagem aqui

Bartek Lipinski
fonte
28
Costumo ver requestLayout sendo chamado diretamente após a invalidez ser chamada, até vejo que isso acontece no código-fonte do Android para coisas como o TextView, mas, de acordo com este diagrama, isso é redundante, certo? Então, existe algum propósito para fazer isso?
Tcox 12/05
5
Bem, essa é uma pergunta interessante, e para ser sincero, eu realmente não sei por que eles chamam os dois métodos, por exemplo TextView. Pensei que talvez eles quisessem desenhar Viewpela última vez antes de alterar seus parâmetros relacionados ao layout, mas realmente não faz sentido se pensarmos sobre essas chamadas serem chamadas na ordem diferente (e elas ligam invalidate()logo depois requestLayout()em TextViewtambém). Talvez mereça outra pergunta no StackOverflow :)?
Bartek Lipinski
14
(1/2) : Eu não acho que você entenda esses dois métodos ( invalidate()e requestLayout()) corretamente. O objetivo desses métodos é dizer Viewque tipo de invalidação (como você a chamou) aconteceu. Não é o caso de Viewdecidir qual caminho seguir depois de chamar um desses métodos. A lógica por trás da escolha do Viewcaminho do ciclo de vida é a escolha do método apropriado para se chamar. Se houver algo relacionado à alteração de tamanho - requestLayout()deve ser chamado, se houver apenas uma alteração visual sem um tamanho alterado - você deve ligar invalidate().
Bartek Lipinski
11
(2/2): se você alterar o tamanho do seu Viewde alguma forma, por exemplo, você atualizaLayoutParams um Viewe modifica-o, mas NÃO chama um requestLayoutou setLayoutParams(que chama requestLayoutinternamente), pode ligar o invalidate()quanto quiser e o Viewnão passará pelo processo de layout da medida e, portanto, não mudará de tamanho. Se você não informar Viewque seu tamanho foi alterado (com uma requestLayoutchamada de método), o Viewassumirá que não, e o onMeasuree onLayoutnão será chamado.
precisa
2
@tcox mais aqui -> stackoverflow.com/questions/35279374/…
Sotti
125

invalidate()

A chamada invalidate()é feita quando você deseja agendar um redesenho da exibição. Isso resultará em onDrawuma chamada eventualmente (em breve, mas não imediatamente). Um exemplo de quando uma visualização personalizada seria chamada é quando uma propriedade de cor de texto ou plano de fundo é alterada.

A vista será redesenhada, mas o tamanho não será alterado.

requestLayout()

Se algo na sua visualização mudar, afetando o tamanho, você deve ligar requestLayout(). Isso será acionado onMeasuree onLayoutnão apenas para essa visualização, mas também para as visualizações pai.

NãorequestLayout() é garantido que aonDraw chamada resulte em (ao contrário do que o diagrama na resposta aceita), por isso é geralmente combinada com invalidate().

invalidate();
requestLayout();

Um exemplo disso é quando um rótulo personalizado tem sua propriedade de texto alterada. O rótulo mudaria de tamanho e, portanto, precisaria ser medido novamente e redesenhado.

forceLayout()

Quando há um requestLayout()chamado em um grupo de visualizações pai, não é necessário recalcular e retransmitir suas visualizações filho. No entanto, se uma criança deve ser incluída na medida e na retransmissão, você pode chamá forceLayout()-la. forceLayout()só funciona em um filho se ocorrer em conjunto com um requestLayout()pai direto. A chamada forceLayout()por si só não terá efeito, pois não aciona uma requestLayout()árvore da visualização.

Leia estas perguntas e respostas para obter uma descrição mais detalhada de forceLayout().

Um estudo mais aprofundado

Suragch
fonte
1
mas não faria mais sentido chamar primeiro requestLayout () e depois invalidar ()?
scholt
2
@ scholt, tanto quanto sei a ordem não importa, então você pode ligar requestLayout()antes, invalidate()se quiser. Ninguém faz um layout ou desenha imediatamente. Em vez disso, eles definem sinalizadores que acabarão resultando em retransmissão e redesenho.
Suragch
27

Aqui você pode encontrar algumas respostas: http://developer.android.com/guide/topics/ui/how-android-draws.html

Para mim, uma chamada invalidate()atualiza apenas a exibição e uma chamada requestLayout()atualiza a exibição e calcula o tamanho da exibição na tela.

François BOURLIEUX
fonte
3
que tal forceLayout () então?
Sdabet
@fiddler, este método apenas define duas bandeiras: PFLAG_FORCE_LAYOUT e PFLAG_INVALIDATED
suitianshi
7
@suitianshi Quais são as consequências de definir as bandeiras então?
Sergey
1
@Sergey A conseqüência parece ser que esse sinalizador substitui o cache de medida (as visualizações usam o cache para medir mais rapidamente no futuro para o mesmo MeasureSpec); veja a linha 18783 do View.java aqui: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim
3

você usa invalidate () em uma exibição que deseja redesenhar, fará com que seu onDraw (Canvas c) seja invocado e requestLayout () fará com que todo o layout de renderização (fase de medição e fase de posicionamento) seja executado novamente. Você deve usá-lo se estiver alterando o tamanho da visualização filho em tempo de execução, mas apenas em casos específicos, como restrições da visualização pai (com isso quero dizer que a altura ou largura do pai são WRAP_CONTENT e, portanto, correspondam à medida dos filhos antes que eles possam envolvê-los novamente)

Nativ
fonte
3

Esta resposta não está correta forceLayout().

Como você pode ver no códigoforceLayout() , apenas marca a exibição como "precisa de uma retransmissão", mas ela não agenda nem aciona essa retransmissão. A retransmissão não ocorrerá até que, em algum momento no futuro, o pai da exibição tenha sido apresentado por algum outro motivo.

Há também um problema muito maior ao usar forceLayout()e requestLayout():

Digamos que você tenha chamado forceLayout()uma visualização. Agora, ao chamar requestLayout()um descendente dessa visão, o Android recursivamente chamará requestLayout()os ancestrais desse descendente. O problema é que ele interromperá a recursão na exibição para a qual você ligou forceLayout(). Portanto, a requestLayout()chamada nunca alcançará a raiz da visualização e, portanto, nunca agendará um passe de layout. Uma subárvore inteira da hierarquia de exibição está aguardando um layout, e chamar requestLayout()qualquer visualização dessa subárvore não causará um layout. Apenas chamar requestLayout()qualquer visualização fora dessa subárvore interromperá o feitiço.

Eu consideraria a implementação de forceLayout()(e como isso afeta requestLayout()a ser quebrado e você nunca deve usar essa função em seu código.

fluidsonic
fonte
1
Oi! Como você criou esse feitiço ? Isso está documentado em algum lugar?
azizbekian
1
Infelizmente, isso não está documentado e provavelmente nem se destina. Eu descobri isso investigando o código Viewe executando o problema sozinho e depurando-o.
fluidsonic
Então, estou curioso sobre o objetivo da forceLayout()API: na verdade, não força uma passagem de layout, apenas altera uma sinalização que está sendo observadaonMeasure() , mas onMeasure()não será chamada a menos que requestLayout()um explícito View#measure()seja chamado. Isso significa que forceLayout()deve ser emparelhado requestLayout(). Por outro lado, por que executar forceLayout()se ainda preciso executar requestLayout()?
azizbekian
@azizbekian não, você não precisa. requestLayout()faz tudo o que forceLayout()também faz.
fluidsonic
1
@ Suragch perdeu esse, obrigado! Análise muito interessante. O uso pretendido forceLayoutfaz sentido. Então, no final, é muito mal nomeado e documentado.
Fluidsonic #
0

invalidate()---> onDraw()do thread da interface do usuário

postInvalidate()---> onDraw()do segmento de segundo plano

requestLayout()---> onMeasure()e onLayout()E não necessariamente onDraw()

  • IMPORTANTE : A chamada desse método não afeta o filho da classe chamada.

forceLayout()---> onMeasure()e onLayout() APENAS SE o pai direto chamou requestLayout().

Ehsan Mashhadi
fonte