Layouts   Part of Android Jetpack.

O layout define a estrutura de uma interface do usuário no aplicativo, assim como a atividade. Todos os elementos do layout são criados usando a hierarquia de View e objetos 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 são geralmente chamados de “widgets” e podem ser uma de muitas subclasses, como Button ou TextView. Os objetos ViewGroup são geralmente 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 em ambiente de execução. O aplicativo pode criar objetos View e ViewGroup (e processar suas propriedades) programaticamente.

Ao declarar a IU no XML, você pode separar a apresentação do seu aplicativo do código que controla o comportamento dele. O uso de arquivos XML também facilita 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 ambiente de execução.

Dica: para depurar o layout no ambiente de execução, use a ferramenta Inspetor de layout.

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 para conter uma TextView vertical 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 Activity é chamado pela biblioteca 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 atributos são considerados “parâmetros do layout”, que são os que descrevem determinadas orientações de layout do objeto View conforme definido pelo objeto ViewGroup pai daquele objeto.

Código

Qualquer objeto View pode ter um código de número inteiro associado para identificar exclusivamente o View dentro da árvore. Ao compilar o aplicativo, esse código é referenciado como um número inteiro, mas o código 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 código, 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 código e identificá-la como um recurso de código. 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 código oferecidos pela biblioteca do Android. Ao referenciar um código 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 código da classe de recursos 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 código 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 códigos 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 código exclusivo.

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

Parâmetros do layout

Os atributos do layout XML chamados layout_something definem parâmetros para a 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 visualizaçõ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 visualização a se redimensionar de acordo com as medidas exigidas pelo conteúdo.
  • match_parent instrui a visualização a assumir o maior tamanho permitido pelo grupo de visualizações pais.

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 independentes de densidade (dp), wrap_content ou match_parent é uma abordagem melhor, porque ajuda a garantir que o aplicativo 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 da visualização pai. 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 preenchimento é expresso em pixels para a esquerda, a direita e as partes superior e inferior da visualização. O preenchimento pode ser usado para compensar o conteúdo da visualização por um número específico de pixels. Por exemplo, um preenchimento à 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 preenchimento, 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 seja possível aninhar um ou mais layouts em outro layout para conseguir o projeto de IU, deve-se procurar manter a hierarquia do layout a menos profunda possível. O layout carrega mais rápido se tiver menos layouts aninhados (uma hierarquia de visualizações grande é melhor do que uma hierarquia de visualizações 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 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 em ambiente de 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 de 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 uma 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 uma TextView.

Por exemplo, se você tiver uma matriz de strings que quer exibir em uma 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:

  • O Context do aplicativo
  • O layout que contém uma TextView para cada string na matriz;
  • A matriz de strings

Em seguida, simplesmente chame setAdapter() na 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 modificar o método toString() para os objetos na matriz. Ou, para criar uma visualização para cada item diferente de uma TextView (por exemplo, se você quiser uma ImageView para cada item da matriz), estenda a classe ArrayAdapter e modifique 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 você 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á à visualização anexada que os dados foram alterados e que ela deve se atualizar.

Processamento de eventos de clique

Para responder a eventos de clique em cada item em um AdapterView, implemente a interface AdapterView.OnItemClickListener. Por 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);

Recursos adicionais

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