Layouts   Parte do Android Jetpack.

O layout define a estrutura de uma interface do usuário no aplicativo, como acontece na atividade. Todos os elementos do layout são criados usando a hierarquia de objetos View e ViewGroup. A View geralmente desenha algo que o usuário pode ver e com que pode interagir. Já um ViewGroup é um contêiner invisível que define a estrutura do layout para View e outros objetos ViewGroup, como pode ser visto na figura 1.

Figura 1. Ilustração de uma hierarquia de visualização, que define o layout de uma IU.

Os objetos View geralmente são chamados de "widgets" e podem ser uma das muitas subclasses, como Button ou TextView. Os objetos ViewGroup geralmente são chamados de layouts e podem ser de um dos muitos tipos que fornecem uma estrutura de layout diferente, como LinearLayout ou ConstraintLayout .

Um layout pode ser declarado de duas maneiras:

  • Declarar elementos da IU em XML. O Android fornece um vocabulário XML direto que corresponde às classes e subclasses de visualização, como as de widgets e layouts.

    Também é possível usar o Layout Editor do Android Studio para criar o layout XML usando uma interface de arrastar e soltar.

  • Instanciar elementos do layout no momento da execução. O aplicativo pode criar objetos View e ViewGroup (e processar suas propriedades) programaticamente.

Ao declarar a IU no XML, é possível separar a apresentação do seu aplicativo do código que controla o comportamento dele. O uso de arquivos XML também facilita conseguir layouts diferentes para diferentes orientações e tamanhos de tela. Isso é discutido em Compatibilidade com diferentes tamanhos de tela.

A biblioteca do Android oferece flexibilidade para usar um ou ambos os métodos para criar a IU do seu aplicativo. Por exemplo, é possível declarar os layouts padrão do aplicativo em XML e, em seguida, modificar o layout no momento da execução.

Dica: para depurar o layout no momento da execução, use a ferramenta Layout Inspector.

Programação do XML

Usando o vocabulário XML do Android, é possível projetar rapidamente layouts de IU e os elementos de tela intrínsecos, do mesmo modo que se cria páginas Web em HTML - com uma série de elementos aninhados.

Cada arquivo de layout deve conter exatamente um elemento raiz, que deve ser um objeto View ou ViewGroup. Com o elemento raiz definido, é possível adicionar objetos ou widgets de layout extras como elementos filho para construir gradualmente uma hierarquia de View que define o layout. Por exemplo, veja um layout XML que usa um LinearLayout vertical para conter um TextView e um Button:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

Após declarar o layout no XML, salve o arquivo com uma extensão .xml no diretório res/layout/ do projeto do Android para compilá-lo adequadamente.

Veja mais informações sobre a sintaxe de um arquivo XML de layout no documento Recursos de layout.

Carregamento do recurso XML

Ao compilar o aplicativo, cada arquivo de layout XML é compilado em um recurso View. Deve-se carregar o recurso de layout do código do aplicativo na implementação de callback Activity.onCreate(). Para isso, chame setContentView(), passando a referência para o recurso de layout na forma: R.layout.layout_file_name. Por exemplo, se o layout XML for salvo como main_layout.xml, será necessário carregá-lo para a Activity desta forma:

Kotlin

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

Java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

O método de callback onCreate() na Atividade é chamado pelo framework do Android quando ela é iniciada. Veja a discussão sobre ciclos de vida no documento Atividades.

Atributos

Cada objeto View e ViewGroup aceita uma variedade própria de atributos XML. Alguns atributos são específicos de um objeto View (por exemplo, TextView aceita o atributo textSize), mas esses atributos também são herdados de objetos View que possam estender essa classe. Alguns são comuns a todos os objetos View porque são herdados da classe View raiz (como o atributo id). Além disso, outros são considerados "parâmetros do layout", que são atributos que descrevem determinadas orientações de layout do objeto View, conforme definido pelo objeto ViewGroup pai daquele objeto.

ID

Qualquer objeto View pode ter um ID de número inteiro associado para identificar exclusivamente o View dentro da árvore. Ao compilar o aplicativo, esse ID é referenciado como um número inteiro, mas normalmente é atribuído no arquivo XML do layout como uma string, no atributo id. É um atributo XML comum a todos os objetos View (definido pela classe View) e você o usará com frequência. A sintaxe de um ID, dentro de uma tag XML, é:

android:id="@+id/my_button"

Um símbolo de arroba (@) no início da string indica que o analisador XML deve analisar e expandir o restante da string de ID e identificá-la como um recurso de ID. O símbolo de mais (+) significa que é um novo nome de recurso que precisa ser criado e adicionado aos recursos (no arquivo R.java). Há diversos outros recursos de ID oferecidos pelo framework do Android. Ao referenciar um ID de recurso do Android, não é necessário ter o símbolo de mais, mas deve-se adicionar o namespace do pacote android da seguinte maneira:

android:id="@android:id/empty"

Com o namespace do pacote android em vigor, podemos referenciar um ID da classe de recursos de android.R em vez de um da classe de recursos locais.

Para criar visualizações e referenciá-las a partir do aplicativo, um modo padrão comum é:

  1. Definir uma visualização/um widget no arquivo de layout e atribuir um ID exclusivo a ela/ele:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
    
  2. Em seguida, criar uma instância do objeto de visualização e capturá-la do layout (normalmente no método onCreate()):

    Kotlin

    val myButton: Button = findViewById(R.id.my_button)
    

    Java

    Button myButton = (Button) findViewById(R.id.my_button);
    

Definir IDs para objetos de visualização é importante ao criar um RelativeLayout. Em um layout relativo, visualizações irmãs podem definir o layout relativo para outra visualização irmã, referenciada pelo ID exclusivo.

Os IDs não precisam ser exclusivos por toda a árvore, mas devem ser exclusivos dentro da parte da árvore em que você está procurando (que pode, com frequência, ser toda a árvore, portanto, é preferível ser totalmente exclusivo sempre que possível).

Observação: com o Android Studio 3.6 e mais recentes, o recurso de vinculação de visualizações pode substituir chamadas de findViewById() e fornece segurança de tipo no tempo de compilação para o código que interage com as visualizações. Use a vinculação de visualizações em vez de findViewById().

Parâmetros do layout

Os atributos do layout XML chamados layout_something definem parâmetros de layout para o View apropriados para o ViewGroup em que reside.

Cada classe ViewGroup implementa uma classe aninhada que estende ViewGroup.LayoutParams. Essa subclasse contém tipos de property que definem o tamanho e a posição de cada visualização filha, conforme necessário para o grupo de visualizações. Como se pode ver na figura 2, um grupo de visualizações pai define parâmetros de layout para cada visualização filha (incluindo o grupo de visualizações filhas).

Figura 2. Visualização de uma hierarquia de exibições com parâmetros de layout associados a cada uma delas.

Cada subclasse LayoutParams tem a própria sintaxe para definir valores. Cada elemento filho deve definir LayoutParams apropriados para o pai, embora possa também definir diferentes LayoutParams para os próprios filhos.

Todos os grupos de visualizações contêm largura e altura (layout_width e layout_height) e cada visualização é obrigatória para defini-las. Muitos LayoutParams também contêm margens e bordas opcionais.

É possível especificar largura e altura com medidas exatas, embora não seja recomendável na maioria dos casos. Em geral, usa-se uma destas constantes para definir a largura e a altura:

  • wrap_content instrui a exibição a redimensionar de acordo com as dimensões exigidas pelo conteúdo.
  • match_parent instrui sua visualização a assumir o maior tamanho permitido pelo grupo de visualizações parental.

Em geral, a especificação de largura e altura de um layout com unidades absolutas, como pixels, não é recomendada. Em vez disso, o uso de medidas relativas como unidades de pixel de densidade independente (dp), wrap_content ou match_parent é uma abordagem melhor, porque ajuda a garantir que o app exiba o conteúdo adequadamente nos diversos tamanhos de tela de dispositivos. Os tipos de medidas aceitos são definidos no documento Recursos disponíveis.

Posição do layout

A forma geométrica de uma visualização é um retângulo. As visualizações têm uma localização, expressa como um par de coordenadas esquerda e superior, além de duas dimensões, expressas como largura e altura. A unidade de localização e de dimensões é o pixel.

É possível recuperar a localização de uma visualização chamando os métodos getLeft() e getTop(). O primeiro retorna a coordenada esquerda, ou X, do retângulo que representa a visualização. O último retorna a coordenada superior, ou Y, do retângulo que representa a visualização. Esses métodos retornam a localização da visualização em relação ao pai correspondente. Por exemplo, quando getLeft() retorna 20, significa que a visualização se localiza 20 pixels à direita da borda esquerda do seu pai direto.

Além disso, diversos métodos de conveniência são oferecidos para evitar cálculos desnecessários, chamados getRight() e getBottom(). Esses métodos retornam as coordenadas das bordas direita e inferior do retângulo que representa a visualização. Por exemplo, chamar getRight() é semelhante ao seguinte cálculo: getLeft() + getWidth().

Tamanho, preenchimento e margens

O tamanho de uma visualização é expresso por largura e altura. As visualizações, na verdade, têm dois pares de valores de largura e altura.

O primeiro par é conhecido como largura medida e altura medida. Essas dimensões definem o tamanho que a visualização terá dentro do parental. As dimensões medidas podem ser conseguidas chamando getMeasuredWidth() e getMeasuredHeight().

O segundo par é simplesmente conhecido como largura e altura ou, às vezes, largura do desenho e altura do desenho. Essas dimensões definem o tamanho real da visualização na tela, na hora do desenho e após o layout. Esses valores podem diferir da largura e da altura medidas. Os valores de largura e altura podem ser conseguidos chamando getWidth() e getHeight().

Para medir as dimensões, a visualização leva em conta o preenchimento. O padding é expresso em pixels para a esquerda, a direita e as partes superior e inferior da visualização. O padding pode ser usado para compensar o conteúdo da visualização por um número específico de pixels. Por exemplo, um padding à esquerda de 2 empurrará o conteúdo da visualização em 2 pixels para a direita da borda esquerda. O preenchimento pode ser definido usando o método setPadding(int, int, int, int) e consultado chamando getPaddingLeft(), getPaddingTop(), getPaddingRight() e getPaddingBottom().

Mesmo que cada visualização possa definir um padding, ela não fornece nenhuma compatibilidade com margens. No entanto, os grupos de visualizações oferecem essa compatibilidade. Consulte ViewGroup e ViewGroup.MarginLayoutParams para ver mais informações.

Para mais informações sobre dimensões, consulte Valores de dimensões.

Layouts comuns

Cada subclasse da classe ViewGroup fornece um modo exclusivo de exibir as visualizações aninhadas dentro dela. Abaixo estão alguns dos tipos de layout mais comuns criados na plataforma Android.

Observação: embora você possa aninhar um ou mais layouts em outro layout para conseguir o design de IU, procure manter esta hierarquia o menos profunda possível. O layout será carregado mais rápido se tiver menos layouts aninhados. Uma hierarquia de visualização grande é melhor do que uma hierarquia de visualização profunda.

Layout linear

É um layout que organiza os filhos em uma única linha horizontal ou vertical. Ele criará uma barra de rolagem se o comprimento da janela exceder o comprimento da tela.

Layout relativo

Permite especificar a localização de objetos filhos relativos entre si (filho A à esquerda do filho B) ou relativos aos pais (alinhados na parte superior do pai).

Visualização da Web

Exibe páginas da Web.

Criação de layouts com um adaptador

Quando o conteúdo do layout é dinâmico ou não predeterminado, é possível usar um layout que torne AdapterView uma subclasse para preencher o layout com visualizações no momento da execução. Uma subclasse da classe AdapterView usa um Adapter para agrupar dados ao layout. O Adapter se comporta como um intermediário entre a fonte dos dados e o layout do AdapterView, o Adapter recupera os dados (de uma fonte como uma matriz ou uma consulta de banco de dados) e converte cada entrada em uma visualização que pode ser adicionada ao layout do AdapterView.

Alguns layouts comuns retornados por um adaptador:

Visualização em lista

Exibe uma lista de rolagem de coluna única.

Visualização em grade

Exibe uma grade de rolagem de colunas e linhas.

Preenchimento da visualização de adaptador com dados

É possível preencher um AdapterView como ListView ou GridView vinculando a instância de AdapterView a um Adapter, que recupera dados de uma fonte externa e cria um View que representa cada entrada de dados.

O Android oferece diversas subclasses de Adapter que são úteis para recuperar diferentes tipos de dados e criar visualizações de um AdapterView. Os dois adaptadores mais comuns são:

ArrayAdapter
Use esse adaptador quando a fonte de dados for uma matriz. Por padrão, ArrayAdapter cria uma visualização para cada item de matriz chamando toString() em cada item e posicionando o conteúdo em um TextView.

Por exemplo, se você tiver uma matriz de strings que quer exibir em um ListView, inicialize um novo ArrayAdapter usando um construtor para especificar o layout de cada string e a matriz de strings:

Kotlin

val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)

Java

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1, myStringArray);

Os argumentos desse construtor são:

  • Seu aplicativo Context
  • O layout que contém um TextView para cada string na matriz
  • A matriz de strings

Em seguida, simplesmente chame setAdapter() em ListView:

Kotlin

val listView: ListView = findViewById(R.id.listview)
listView.adapter = adapter

Java

ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);

Para personalizar a aparência de cada item, é possível substituir o método toString() para os objetos na matriz. Ou, para criar uma visualização para cada item diferente de um TextView (por exemplo, se você quiser um ImageView para cada item da matriz), estenda a classe ArrayAdapter e substitua getView() para retornar o tipo de visualização que quer para cada item.

SimpleCursorAdapter
Use esse adaptador quando os dados vierem de um Cursor. Ao usar SimpleCursorAdapter, é necessário especificar um layout a usar para cada linha no Cursor e quais colunas no Cursor precisam ser inseridas em determinadas visualizações do layout. Por exemplo, se quiser criar uma lista de nome e número de telefone de pessoas, poderá executar uma consulta que retorna um Cursor que contém uma linha para cada pessoa e colunas para os nomes e números. Depois, crie uma matriz de strings que especifique quais colunas do Cursor estarão no layout para cada resultado e uma matriz de números inteiros especificando as visualizações correspondentes em que cada coluna deve ser colocada:

Kotlin

val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                          ContactsContract.CommonDataKinds.Phone.NUMBER)
val toViews = intArrayOf(R.id.display_name, R.id.phone_number)

Java

String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                        ContactsContract.CommonDataKinds.Phone.NUMBER};
int[] toViews = {R.id.display_name, R.id.phone_number};

Ao instanciar o SimpleCursorAdapter, passe o layout a usar para cada resultado, o Cursor contendo os resultados e estas duas matrizes:

Kotlin

val adapter = SimpleCursorAdapter(this,
        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
val listView = getListView()
listView.adapter = adapter

Java

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
ListView listView = getListView();
listView.setAdapter(adapter);

Em seguida, o SimpleCursorAdapter cria uma visualização de cada linha no Cursor usando o layout fornecido por meio da inserção de cada item de fromColumns na visualização toViews correspondente.

.

Se, durante o curso de vida do aplicativo, você mudar os dados subjacentes lidos pelo adaptador, chame notifyDataSetChanged(). Isso notificará a visualização anexada de que os dados foram mudados e que ela precisa ser atualizada.

Processamento de eventos de clique

Para responder a eventos de clique em cada item em um AdapterView, implemente a interface AdapterView.OnItemClickListener. Exemplo:

Kotlin

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click
}

Java

// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click
    }
};

listView.setOnItemClickListener(messageClickedHandler);

Outros recursos

Os layouts são usados no app de demonstração Sunflower.