Explicação da visualização personalizada do onMeasure
316
Eu tentei fazer componente personalizado. Estendi a Viewclasse e fiz alguns desenhos no onDrawmétodo substituído. Por que eu preciso substituir onMeasure? Se não, tudo parecia certo. Alguém pode explicar isso? Como devo escrever meu onMeasuremétodo? Eu já vi alguns tutoriais, mas cada um é um pouco diferente do outro. Às vezes eles ligam super.onMeasureno final, às vezes usam setMeasuredDimensione não ligam. Onde está a diferença?
Afinal, eu quero usar vários exatamente os mesmos componentes. Adicionei esses componentes ao meu XMLarquivo, mas não sei quão grandes eles devem ser. Desejo definir sua posição e tamanho mais tarde (por que preciso definir o tamanho onMeasurese, onDrawquando o desenho, também está funcionando) na classe de componente personalizada. Quando exatamente eu preciso fazer isso?
onMeasure()é a sua oportunidade de informar ao Android qual o tamanho que você deseja que sua visualização personalizada dependa das restrições de layout fornecidas pelos pais; também é a oportunidade da sua exibição personalizada para saber quais são essas restrições de layout (caso você queira se comportar de maneira diferente em uma match_parentsituação e em uma wrap_contentsituação). Essas restrições são agrupadas nos MeasureSpecvalores que são passados para o método. Aqui está uma correlação aproximada dos valores de modo:
EXATAMENTE significa que o valor layout_widthou layout_heightfoi definido para um valor específico. Você provavelmente deve ter sua visualização desse tamanho. Isso também pode ser acionado quando match_parentusado, para definir o tamanho exatamente para a visualização pai (isso depende do layout na estrutura).
AT_MOST normalmente significa que o valor layout_widthou layout_heightfoi definido como match_parentou wrap_contentonde é necessário um tamanho máximo (isso depende do layout na estrutura), e o tamanho da dimensão pai é o valor. Você não deve ser maior que esse tamanho.
NÃO ESPECIFICADO normalmente significa que o valor layout_widthou layout_heightfoi definido como wrap_contentsem restrições. Você pode ter o tamanho que desejar. Alguns layouts também usam esse retorno de chamada para descobrir o tamanho desejado antes de determinar quais especificações realmente serão enviadas novamente em uma segunda solicitação de medida.
O contrato que existe onMeasure()é que setMeasuredDimension()DEVE ser chamado no final com o tamanho que você deseja que a vista seja. Esse método é chamado por todas as implementações da estrutura, incluindo a implementação padrão encontrada em View, e é por isso que é seguro chamar, superse isso se encaixa no seu caso de uso.
Concedido, porque a estrutura aplica uma implementação padrão, pode não ser necessário substituir esse método, mas você pode ver recortes nos casos em que o espaço de exibição é menor que o seu conteúdo, se não o fizer, e se você definir o seu Na visualização personalizada, wrap_contentnas duas direções, a visualização pode não aparecer, porque a estrutura não sabe o tamanho dela!
Geralmente, se você está substituindo Viewe não outro widget existente, provavelmente é uma boa idéia fornecer uma implementação, mesmo que seja tão simples quanto algo como isto:
@Overrideprotectedvoid onMeasure(int widthMeasureSpec,int heightMeasureSpec){int desiredWidth =100;int desiredHeight =100;int widthMode =MeasureSpec.getMode(widthMeasureSpec);int widthSize =MeasureSpec.getSize(widthMeasureSpec);int heightMode =MeasureSpec.getMode(heightMeasureSpec);int heightSize =MeasureSpec.getSize(heightMeasureSpec);int width;int height;//Measure Widthif(widthMode ==MeasureSpec.EXACTLY){//Must be this size
width = widthSize;}elseif(widthMode ==MeasureSpec.AT_MOST){//Can't be bigger than...
width =Math.min(desiredWidth, widthSize);}else{//Be whatever you want
width = desiredWidth;}//Measure Heightif(heightMode ==MeasureSpec.EXACTLY){//Must be this size
height = heightSize;}elseif(heightMode ==MeasureSpec.AT_MOST){//Can't be bigger than...
height =Math.min(desiredHeight, heightSize);}else{//Be whatever you want
height = desiredHeight;}//MUST CALL THIS
setMeasuredDimension(width, height);}
Hey @Devunwired boa explicação o melhor que eu li até agora. Sua explicação respondeu a muitas perguntas que eu tinha e tirei algumas dúvidas, mas ainda resta uma: Qual é a minha visão personalizada dentro de um ViewGroup juntamente com outras Views (não importa quais tipos), que o ViewGroup receberá todos os seus filhos e para cada uma delas investigar sua restrição de LayoutParams e pedir a cada criança que se avalie de acordo com suas restrições?
faraó
47
Observe que esse código não funcionará se você substituir a medição de qualquer subclasse de ViewGroup. Suas subvisões não serão exibidas e todas terão um tamanho de 0x0. Se você precisar substituir onMeasure de um ViewGroup personalizado, altere widthMode, widthSize, heightMode e heightSize, compile-os novamente para measureSpecs usando MeasureSpec.makeMeasureSpec e passe os números inteiros resultantes para super.onMeasure.
Alexey19
1
Resposta fantástica. Observe que, de acordo com a documentação do Google, é de responsabilidade do View lidar com o preenchimento.
jonstaff
4
Sobre c ** p complicado que faz do Android um sistema de layout doloroso para trabalhar. Eles poderiam ter apenas teve getParent () get *** () ....
Oliver Dixon
2
Existem métodos auxiliares na Viewclasse, chamados resolveSizeAndStatee resolveSize, que devem fazer o que as cláusulas 'if' fazem - eu os achei úteis, especialmente se você tiver que escrever esses FIs frequentemente.
stan0
5
na verdade, sua resposta não está completa, pois os valores também dependem do contêiner de embalagem. No caso de layouts relativos ou lineares, os valores se comportam assim:
EXATAMENTE match_parent é EXATAMENTE + tamanho do pai
AT_MOST wrap_content resulta em um AT_MOST MeasureSpec
NÃO ESPECIFICADO nunca disparado
No caso de uma exibição de rolagem horizontal, seu código funcionará.
Se você achar que alguma resposta aqui está incompleta, adicione-a em vez de fornecer uma resposta parcial.
Michaël
1
Bom por vincular isso a como os layouts funcionam, mas, no meu caso, onMeasure é chamado três vezes para minha visualização personalizada. A vista em questão tinha uma altura de wrap_content e uma largura ponderada (largura = 0, peso = 1). A primeira chamada foi ESPECIFICADA / NÃO ESPECIFICADA, a segunda teve AT_MOST / EXATAMENTE e a terceira teve EXATAMENTE / EXATAMENTE.
22420 William Th Mallard
0
Se você não precisar alterar algo no Measure - não há absolutamente nenhuma necessidade de substituí-lo.
O código Devunwired (a resposta selecionada e mais votada aqui) é quase idêntico ao que a implementação do SDK já faz por você (e eu verifiquei - fazia isso desde 2009).
View
classe, chamadosresolveSizeAndState
eresolveSize
, que devem fazer o que as cláusulas 'if' fazem - eu os achei úteis, especialmente se você tiver que escrever esses FIs frequentemente.na verdade, sua resposta não está completa, pois os valores também dependem do contêiner de embalagem. No caso de layouts relativos ou lineares, os valores se comportam assim:
No caso de uma exibição de rolagem horizontal, seu código funcionará.
fonte
Se você não precisar alterar algo no Measure - não há absolutamente nenhuma necessidade de substituí-lo.
O código Devunwired (a resposta selecionada e mais votada aqui) é quase idêntico ao que a implementação do SDK já faz por você (e eu verifiquei - fazia isso desde 2009).
Você pode verificar o método onMeasure aqui :
Substituir o código SDK a ser substituído pelo mesmo código não faz sentido.
A parte deste documento oficial que afirma que "o padrão onMeasure () sempre definirá um tamanho de 100x100" - está errada.
fonte