O Android oferece um modelo de componentes sofisticado e eficiente para criar a interface com base no
as classes fundamentais de layout
View
e
ViewGroup
. A plataforma inclui
uma variedade de subclasses pré-criadas View
e ViewGroup
, chamadas de widgets,
layouts, respectivamente, que podem ser usados para construir sua IU.
Uma lista parcial de widgets disponíveis inclui Button
,
TextView
,
EditText
,
ListView
,
CheckBox
,
RadioButton
,
Gallery
,
Spinner
e, para fins especiais,
AutoCompleteTextView
,
ImageSwitcher
e
TextSwitcher
.
Entre os layouts disponíveis estão
LinearLayout
,
FrameLayout
,
RelativeLayout
,
e outros. Para mais exemplos, consulte
Layouts comuns.
Se nenhum dos widgets ou layouts pré-criados atender às suas necessidades, é possível criar seus próprios widgets
View
. Se você só precisar fazer pequenos ajustes em um widget ou
layout, você pode criar uma subclasse do widget ou layout e substituir os métodos dele.
Criar suas próprias subclasses View
oferece controle preciso sobre a aparência e
de um elemento da tela. Para dar uma ideia do controle que você tem com as visualizações personalizadas, veja aqui
alguns exemplos do que você pode fazer com elas:
-
Você pode criar um tipo
View
de renderização personalizada, por exemplo, um nome controle" botão, renderizado usando gráficos 2D, que se assemelha a um controle eletrônico analógico. -
Você pode combinar um grupo de componentes
View
em um novo componente único, talvez para faça algo como uma caixa de combinação (uma combinação de lista pop-up e campo de texto de entrada livre), um controle seletor de painel duplo (um painel esquerdo e direito com uma lista em cada um deles, em que é possível reatribuir qual item está em qual lista) e assim por diante. -
É possível substituir a forma como um componente
EditText
é renderizado na tela. A O app de exemplo do Bloco de notas usa isso para criar uma página de bloco de notas com linhas. - É possível capturar outros eventos, como pressionamentos de teclas, e manipulá-los de maneira personalizada, como quanto a um jogo.
As seções a seguir explicam como criar visualizações personalizadas e usá-las no aplicativo. Para
informações de referência detalhadas, consulte a
View
.
A abordagem básica
Confira uma visão geral de alto nível do que você precisa saber para criar seu próprio View
componentes:
-
Estenda uma classe
View
ou subclasse com sua própria classe. -
Modifique alguns dos métodos da superclasse. Os métodos da superclasse que serão substituídos começam com
on
: por exemplo,onDraw()
,onMeasure()
, eonKeyDown()
. Isso é semelhante aos eventoson
emActivity
ouListActivity
que você do ciclo de vida e de outros hooks de funcionalidade. - Use sua nova classe de extensão. Depois de concluído, você pode usar sua nova classe de extensão no lugar de a visualização em que ele foi baseado.
Componentes totalmente personalizados
Você pode criar componentes gráficos totalmente personalizados que aparecem querem. Talvez você queira um medidor VU gráfico que se pareça com um antigo medidor analógico ou com uma visualização de texto para onde você pode cantar. em que uma bola saltitante se move ao longo das palavras enquanto você canta com uma máquina de karaokê. Talvez você queira algo que os componentes integrados não conseguem, não importa como sejam combinados.
Felizmente, é possível criar componentes que se parecem e se comportam da maneira que você quiser, com limitações por sua imaginação, pelo tamanho da tela e pela capacidade de processamento disponível, tendo em mente que seu seu aplicativo pode precisar ser executado em algo com muito menos energia do que seu computador estação de trabalho.
Para criar um componente totalmente personalizado, considere o seguinte:
-
A visualização mais genérica que você pode estender é
View
. Portanto, você geralmente começa estendendo para criar seu novo supercomponente. - Você pode fornecer um construtor, que pode pegar atributos e parâmetros do XML, e você pode consumir seus próprios atributos e parâmetros, como a cor e o alcance do medidor VU ou a largura e o amortecimento da agulha.
- Você provavelmente deseja criar seus próprios listeners de eventos, acessadores de propriedades e modificadores, bem como comportamento mais sofisticado na sua classe de componentes.
-
É provável que você queira substituir
onMeasure()
e também possa precisar substituaonDraw()
se quiser que o componente mostre algo. Embora ambos tenham comportamento padrão, aonDraw()
padrão não faz nada, e aonMeasure()
sempre define um tamanho de 100 x 100, o que você provavelmente não quer. -
Você também pode substituir outros métodos
on
, conforme necessário.
Estender onDraw() e onMeasure()
O método onDraw()
entrega uma
Canvas
em que é possível
implementar o que você quiser: gráficos 2D, outros componentes padrão ou personalizados, texto estilizado ou
qualquer outra coisa em que você possa pensar.
onMeasure()
está um pouco mais envolvido. onMeasure()
é uma parte importante
do contrato de renderização entre o componente e o contêiner. onMeasure()
precisa ser
substituído para informar com eficiência e precisão as medidas das partes contidas. Isso é
se torna um pouco mais complexo pelos requisitos de limite do pai, que são passados para a
método onMeasure()
e pelo requisito de chamar o
setMeasuredDimension()
com a largura e a altura medidas quando forem
calculada. Se você não chamar esse método a partir de um método onMeasure()
substituído, ele
resulta em uma exceção no momento da medição.
De modo geral, a implementação de onMeasure()
é semelhante a esta:
-
O método
onMeasure()
substituído é chamado com largura e altura especificações, que são tratados como requisitos para as restrições de largura e altura medidas que você produz.widthMeasureSpec
eheightMeasureSpec
são códigos inteiros que representam dimensões. Uma referência completa ao tipo de restrições que essas especificações podem exigir podem ser encontradas na documentação de referência emView.onMeasure(int, int)
Esta documentação de referência também explica toda a operação de medição. -
O método
onMeasure()
do componente calcula a largura e a altura de uma medida. que são necessários para renderizar o componente. Ele precisa tentar respeitar as especificações passadas embora possa excedê-los. Nesse caso, o familiar responsável pode escolher o que fazer, incluindo recorte, rolagem, geração de uma exceção ou solicitação àonMeasure()
para tentar novamente talvez com especificações de medição diferentes. -
Quando a largura e a altura forem calculadas, chame o método
setMeasuredDimension(int width, int height)
com os valores calculados medições. Se isso não for feito, haverá uma exceção.
Confira um resumo de outros métodos padrão que o framework chama em visualizações:
Categoria | Métodos | Descrição |
---|---|---|
Criação | Construtores | Há uma forma do construtor que é chamada quando a visualização é criada usando o código e um formulário que é chamado quando a visualização é inflada de um arquivo de layout. A segunda forma analisa e aplica atributos definidos no arquivo de layout. |
|
Chamado depois que uma visualização e todos os filhos são inflados a partir do XML. | |
Layout |
|
Chamado para determinar os requisitos de tamanho da visualização e de todos os filhos. |
|
Chamado quando a visualização precisa atribuir um tamanho e uma posição a todos os filhos. | |
|
Chamado quando o tamanho da visualização é alterado. | |
Desenho |
|
Chamado quando a visualização precisa renderizar o conteúdo. |
Processamento de eventos |
|
Chamado quando ocorre um evento de pressionamento de tecla. |
|
Chamado quando ocorre um evento de liberação de tecla. | |
|
Chamado quando ocorre um evento de movimento do trackball. | |
|
Chamado quando ocorre um evento de movimento da tela touch. | |
Foco |
|
Chamado quando a visualização ganha ou perde o foco. |
|
Chamado quando a janela que contém a visualização ganha ou perde o foco. | |
Anexo |
|
Chamado quando a visualização é anexada a uma janela. |
|
Chamado quando a visualização é removida da janela. | |
|
Chamado quando a visibilidade da janela que contém a visualização é alterada. |
Controles compostos
Se você não quiser criar um componente completamente personalizado, mas quiser colocar
um componente reutilizável, que consiste em um grupo de controles existentes e, em seguida, criando um composto
componente (ou controle composto) pode ser melhor. Em resumo, isso reúne vários outros
ou visualizações atômicas em um grupo lógico de itens que podem ser tratados como uma única coisa.
Por exemplo, uma caixa de combinação pode ser uma combinação de um campo EditText
de linha única
e um botão adjacente com uma lista pop-up anexada. Se o usuário tocar no botão e selecionar algo
a lista, ele preenche o campo EditText
, mas eles também podem digitar algo
diretamente no EditText
, se preferirem.
No Android, há duas outras visualizações prontamente disponíveis para fazer isso: Spinner
e
AutoCompleteTextView
. Independentemente disso, o conceito de caixa de combinação é um bom exemplo.
Para criar um componente composto, faça o seguinte:
-
Assim como com um
Activity
, use a abordagem declarativa (baseada em XML) para criar os componentes contidos ou aninhá-los de maneira programática no código. A o ponto de partida comum é algum tipo deLayout
, então crie uma classe que estenda umaLayout
: No caso de uma caixa de combinação, você pode usar umLinearLayout
com horizontal. É possível aninhar outros layouts dentro, de modo que o componente composto possa ser arbitrariamente complexa e estruturada. -
No construtor da nova classe, use os parâmetros esperados pela superclasse e transmita
primeiro para o construtor da superclasse. Depois, configure as outras visualizações para usar
no novo componente. É aqui que você cria o campo
EditText
e a lista pop-up. Você pode introduzir seus próprios atributos e parâmetros no XML que seu que o construtor pode extrair e usar. -
Opcionalmente, crie listeners para eventos que as visualizações contidas podem gerar. Um exemplo é uma
do listener do item de lista para atualizar o conteúdo do
EditText
se for feita uma seleção de lista. -
Como opção, crie suas próprias propriedades com acessadores e modificadores. Por exemplo, deixe
O valor
EditText
pode ser definido inicialmente no componente e consultar o conteúdo dele quando necessários. -
Opcionalmente, substitua
onDraw()
eonMeasure()
. Isso geralmente não é necessário quando Estenda umaLayout
, já que o layout tem um comportamento padrão que provavelmente funciona bem. -
Se quiser, substitua outros métodos
on
, comoonKeyDown()
, para escolher determinados valores padrão da lista pop-up de uma caixa de combinação quando uma determinada tecla é tocada.
Há vantagens em usar um Layout
como base para um controle personalizado,
incluindo o seguinte:
- É possível especificar o layout usando os arquivos XML declarativos, assim como em uma tela de atividade, ou criar visualizações de maneira programática e aninhá-las no layout do código.
-
Os métodos
onDraw()
eonMeasure()
, além da maioria dos outroson
têm um comportamento adequado, então não é necessário substituí-los. - É possível construir rapidamente visualizações compostas arbitrariamente complexas e reutilizá-las como se fossem uma componente único.
Modificar um tipo de visualização existente
Se houver um componente semelhante ao que você quer, estenda esse componente e substitua
o comportamento que você quer mudar. Você pode fazer tudo o que faz com uma
mas, ao começar com uma classe mais especializada na hierarquia View
, é possível
obter algum comportamento que faça o que você deseja sem custo financeiro.
Por exemplo, o
Bloco de notas
exemplo de app demonstra muitos aspectos do uso da plataforma Android. Entre eles está a extensão
EditText
para criar um bloco de notas com linhas. Este não é um exemplo perfeito, e as APIs para
fazer isso pode mudar, mas isso demonstra os princípios.
Importe a amostra do Bloco de notas para o Android Studio, caso ainda não tenha feito isso, ou consulte a
usando o link fornecido. Mais especificamente, consulte a definição de LinedEditText
.
no
NoteEditor.java
.
Veja alguns itens a serem observados neste arquivo:
-
A definição
A classe é definida com a seguinte linha:
public static class LinedEditText extends EditText
A
LinedEditText
é definida como uma classe interna noNoteEditor
. atividade, mas é pública para que possa ser acessada comoNoteEditor.LinedEditText
de fora da classeNoteEditor
.Além disso,
LinedEditText
éstatic
, o que significa que não gera a os chamados "métodos sintéticos" que permitem acessar dados da classe mãe. Ou seja, se comporta como uma classe separada em vez de algo fortemente relacionado aNoteEditor
. Essa é uma forma mais limpa de criar classes internas se elas não precisarem de acesso ao estado pela classe externa. Ela mantém a classe gerada pequena e permite que ela seja usada facilmente por outros classes.LinedEditText
estendeEditText
, que é a visualização a ser personalizada em neste caso. Quando você terminar, a nova classe poderá substituir umEditText
normal visualização. -
Inicialização de classes
Como sempre, a superclasse é chamada primeiro. Este não é um construtor padrão, mas é um parametrizado. O
EditText
é criado com esses parâmetros quando é inflada de um arquivo de layout XML. Assim, o construtor precisa pegá-los e passá-los para o construtor da superclasse também. -
Métodos modificados
Este exemplo substitui apenas o método
onDraw()
, mas talvez seja necessário modificar enquanto cria seus próprios componentes personalizados.Para este exemplo, substituir o método
onDraw()
permite que você pinte as linhas azuis na a tela de visualizaçãoEditText
. A tela é transmitida ao servidoronDraw()
. O métodosuper.onDraw()
é chamado antes da termina. O método da superclasse precisa ser invocado. Nesse caso, invoque-a no final, após você pinta as linhas que quer incluir. -
Componente personalizado
Agora você tem seu componente personalizado, mas como pode usá-lo? No exemplo do Bloco de notas, o componente personalizado é usado diretamente do layout declarativo. Portanto, observe
note_editor.xml
nores/layout
pasta:<view xmlns:android="http://schemas.android.com/apk/res/android" class="com.example.android.notepad.NoteEditor$LinedEditText" android:id="@+id/note" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:padding="5dp" android:scrollbars="vertical" android:fadingEdge="vertical" android:gravity="top" android:textSize="22sp" android:capitalize="sentences" />
O componente personalizado é criado como uma visualização genérica no XML, e a classe é especificada usando o pacote completo. A classe interna definida é referenciada usando o
NoteEditor$LinedEditText
, que é uma forma padrão de se referir a objetos da linguagem de programação Java.Se o componente de visualização personalizado não estiver definido como uma classe interna, você poderá declarar a visualização com o nome do elemento XML e excluir o atributo
class
. Por exemplo:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
A classe
LinedEditText
agora é um arquivo de classe separado. Quando o está aninhada na classeNoteEditor
. Essa técnica não funciona.Os outros atributos e parâmetros na definição são os passados para a classe personalizada construtor de componente e, em seguida, transmitido para o construtor
EditText
. Portanto, eles são os mesmos parâmetros que você usa para uma visualizaçãoEditText
. É possível adicionar seus próprios parâmetros.
Criar componentes personalizados é uma tarefa complicada se você precisar que ela seja.
Um componente mais sofisticado pode substituir ainda mais métodos on
e introduzir a
próprios métodos auxiliares, personalizando substancialmente suas propriedades e seu comportamento. O único limite é que
imaginação e o que você precisa que o componente faça.