Layouts em visualizações

Experimente trabalhar com o Compose
O Jetpack Compose é o kit de ferramentas de IU recomendado para Android. Aprenda a trabalhar com layouts no Compose.

Um layout define a estrutura de uma interface do usuário no app, como em uma atividade. Todos os elementos no layout são criados usando uma hierarquia de View e ViewGroup objetos. Uma View geralmente desenha algo que o usuário pode ver e interagir. Um ViewGroup é um contêiner invisível que define a estrutura de layout para View e outros ViewGroup objetos, conforme mostrado na Figura 1.

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

Os objetos View são chamados de widgets e podem ser uma das muitas subclasses, como Button ou TextView. Os ViewGroup objetos geralmente são chamados de layouts e podem ser 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 de IU em XML. O Android fornece um vocabulário XML direto que corresponde às classes e subclasses View, 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 app pode criar View e ViewGroup objetos e manipular as propriedades deles de forma programática.

Declarar a interface em XML permite separar a apresentação do app 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 mais detalhes em Compatibilidade com diferentes tamanhos de tela.

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

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 da Web em HTML com uma série de elementos aninhados.

Cada arquivo de layout deve conter exatamente um elemento raiz, que deve ser a View ou ViewGroup objeto. Depois de definir o elemento raiz, é 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, confira 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>

Depois de declarar o layout em XML, salve o arquivo com a extensão .xml no diretório res/layout/ do projeto do Android para que ele seja compilado corretamente.

Para mais informações sobre a sintaxe de um arquivo XML de layout, consulte Recurso de layout.

Carregar o recurso XML

Ao compilar o aplicativo, cada arquivo de layout XML é compilado em um recurso View. Carregue o recurso de layout na implementação de callback Activity.onCreate() do app. Para isso, chame setContentView(), transmitindo a referência para o recurso de layout no formato: R.layout.layout_file_name. Por exemplo, se o layout XML for salvo como main_layout.xml, carregue-o para sua Activity da seguinte maneira:

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 framework do Android chama o método de callback onCreate() na sua Activity quando a Activity é iniciada. Para mais informações sobre os ciclos de vida das atividades, consulte Introdução às 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. No entanto, esses atributos também são herdados de objetos View que estendem essa classe. Alguns são comuns a todos os objetos View, porque são herdados da classe View raiz, como o atributo id. 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 a ele para identificar exclusivamente a View na árvore. Quando o app é compilado, esse ID é referenciado como um número inteiro, mas o ID normalmente é atribuído no arquivo XML de layout como uma string no atributo id. Esse é um atributo XML comum a todos os objetos View e é definido pela classe View. Ele é usado com muita frequência. A sintaxe de um ID dentro de uma tag XML é a seguinte:

android:id="@+id/my_button"

O símbolo at (@) no início da string indica que o analisador XML analisa e expande o restante da string de ID e a identifica como um recurso de ID. O símbolo mais (+) significa que esse é um novo nome de recurso que precisa ser criado e adicionado aos recursos no arquivo R.java.

O framework do Android oferece muitos outros recursos de ID. Ao referenciar um ID de recurso do Android, você não precisa do símbolo mais, mas precisa adicionar o namespace do pacote android da seguinte maneira:

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

O namespace do pacote android indica que você está referenciando um ID da classe de recursos android.R, em vez da classe de recursos local.

Para criar visualizações e referenciá-las no app, use um padrão comum da seguinte maneira:

  1. Defina uma visualização no arquivo de layout e atribua a ela um ID exclusivo, como no exemplo a seguir:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
  2. Crie uma instância do objeto de visualização e capture-o no layout, normalmente no onCreate() método, conforme mostrado no exemplo a seguir:

    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.

Um ID não precisa ser exclusivo em toda a árvore, mas precisa ser exclusivo na parte da árvore que você pesquisa. Muitas vezes, pode ser a árvore inteira. Portanto, é melhor torná-la exclusiva quando possível.

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 propriedade que definem o tamanho e a posição de cada visualização filha, conforme necessário para o grupo de visualizações. Conforme mostrado na Figura 2, o 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ção com parâmetros de layout associados a cada visualização.

Cada subclasse LayoutParams tem a própria sintaxe para definir valores. Cada elemento filho precisa definir um LayoutParams adequado para o pai, embora também possa definir um LayoutParams diferente para os próprios filhos.

Todos os grupos de visualizações incluem largura e altura, usando layout_width e layout_height, e cada visualização é obrigatória para defini-las. Muitos LayoutParams incluem margens e bordas opcionais.

É possível especificar largura e altura com medidas exatas, mas talvez você não queira fazer isso com frequência. Na maioria das vezes, você usa uma dessas constantes para definir a largura ou a altura:

  • wrap_content: instrui a exibição a redimensionar de acordo com as dimensões exigidas pelo conteúdo.
  • match_parent: instrui a visualização a ficar tão grande quanto a visualização em grupo pai permitir.

Em geral, não recomendamos especificar a largura e a altura de um layout usando unidades absolutas, como pixels. Uma abordagem melhor é usar medidas relativas, como unidades de pixel de densidade independente (dp), wrap_content ou match_parent, porque isso ajuda o app a ser exibido corretamente em vários tamanhos de tela de dispositivos. Os tipos de medidas aceitos são definidos no Recurso de layout.

Posição do layout

Uma visualização tem geometria retangular. Ela tem 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 invocando os métodos getLeft() e getTop(). O primeiro retorna a coordenada esquerda (x) do retângulo que representa a visualização. O segundo retorna a coordenada superior (y) do retângulo que representa a visualização. Esses métodos retornam a localização da visualização em relação ao pai. 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, há métodos convenientes para evitar cálculos desnecessários: 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. Uma visualização tem 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 é 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. É possível usar o preenchimento para compensar o conteúdo da visualização por um número específico de pixels. Por exemplo, um preenchimento à esquerda de dois pixels empurra o conteúdo da visualização dois 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().

Embora uma visualização possa definir um preenchimento, ela não aceita margens. No entanto, os grupos de visualizações aceitam margens. Consulte ViewGroup e ViewGroup.MarginLayoutParams para mais informações.

Para mais informações sobre dimensões, consulte Dimensão.

Além de definir margens e preenchimento de forma programática, também é possível defini-los nos layouts XML, conforme mostrado no exemplo a seguir:

  <?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:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  

O exemplo anterior mostra a aplicação de margem e preenchimento. O TextView tem margens e preenchimento uniformes aplicados em toda a volta, e o Button mostra como é possível aplicá-los de forma independente a diferentes bordas.

Layouts comuns

Cada subclasse da classe ViewGroup fornece um modo exclusivo de exibir as visualizações aninhadas dentro dela. O tipo de layout mais flexível e aquele que oferece as melhores ferramentas para manter a hierarquia de layout rasa é ConstraintLayout.

A seguir, alguns dos tipos de layout comuns integrados à plataforma Android.

Criar um layout linear

Organiza os filhos em uma única linha horizontal ou vertical e cria uma barra de rolagem se o comprimento da janela exceder o comprimento da tela.

Criar apps da Web no WebView

Exibe páginas da Web.

Criar listas dinâmicas

Quando o conteúdo do layout é dinâmico ou não predeterminado, é possível usar RecyclerView ou uma subclasse de AdapterView. RecyclerView geralmente é a melhor opção, porque usa a memória de forma mais eficiente do que AdapterView.

Os layouts comuns possíveis com RecyclerView e AdapterView incluem o seguinte:

Lista

Exibe uma lista de rolagem de coluna única.

Grade

Exibe uma grade de rolagem de colunas e linhas.

RecyclerView oferece mais possibilidades e a opção de criar um gerenciador de layout personalizado.

Preencher uma visualização de adaptador com dados

É possível preencher um AdapterView como ListView ou GridView por vincular a instância 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 da matriz chamando toString() em cada item e colocando o conteúdo em um 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 para esse construtor são os seguintes:

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

Em seguida, chame setAdapter() na sua 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, substitua o toString() método dos objetos na matriz. Ou, para criar uma visualização para cada item que seja algo 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 você quer para cada item.

SimpleCursorAdapter
Use esse adaptador quando os dados vierem de um Cursor. Ao usar SimpleCursorAdapter, especifique um layout a ser usado para cada linha no Cursor e quais colunas no Cursor você quer inserir nas 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 precisa 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, transmita o layout a ser usado para cada resultado, o Cursor que contém os resultados e essas 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 fromColumns item na visualização toViews correspondente.

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

Processar eventos de clique

Para responder a eventos de clique em cada item em um AdapterView implementando a AdapterView.OnItemClickListener interface. 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

Saiba como os layouts são usados no app de demonstração Sunflower no GitHub.