Respostas:
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 :
TextView
. Pensei que talvez eles quisessem desenhar View
pela ú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 TextView
também). Talvez mereça outra pergunta no StackOverflow :)?
invalidate()
e requestLayout()
) corretamente. O objetivo desses métodos é dizer View
que tipo de invalidação (como você a chamou) aconteceu. Não é o caso de View
decidir qual caminho seguir depois de chamar um desses métodos. A lógica por trás da escolha do View
caminho 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()
.
View
de alguma forma, por exemplo, você atualizaLayoutParams
um View
e modifica-o, mas NÃO chama um requestLayout
ou setLayoutParams
(que chama requestLayout
internamente), pode ligar o invalidate()
quanto quiser e o View
não passará pelo processo de layout da medida e, portanto, não mudará de tamanho. Se você não informar View
que seu tamanho foi alterado (com uma requestLayout
chamada de método), o View
assumirá que não, e o onMeasure
e onLayout
não será chamado.
invalidate()
A chamada invalidate()
é feita quando você deseja agendar um redesenho da exibição. Isso resultará em onDraw
uma 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 onMeasure
e onLayout
nã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()
.
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.
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.
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)
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.
View
e executando o problema sozinho e depurando-o.
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()
?
requestLayout()
faz tudo o que forceLayout()
também faz.
forceLayout
faz sentido. Então, no final, é muito mal nomeado e documentado.
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()
forceLayout()
---> onMeasure()
e onLayout()
APENAS SE o pai direto chamou requestLayout()
.