Layouts

O layout define a estrutura visual para uma interface do usuário, como a IU de uma atividade ou de um widget de aplicativo. É possível declarar um layout de dois modos:

  • Declarar elementos da IU em XML. O Android fornece um vocabulário XML direto que corresponde às classes e subclasses de View, como as de widgets e layouts.
  • Instanciar elementos do layout em tempo de execução. O aplicativo pode criar objetos View e ViewGroup (e processar suas propriedades) programaticamente.

A estrutura do Android dá a flexibilidade de usar um desses métodos ou ambos para declarar e gerenciar a IU do aplicativo. Por exemplo, você pode declarar os layouts padrão do aplicativo em XML, incluindo os elementos da tela que aparecerão neles e em suas propriedades. Em seguida, você poderia adicionar código ao aplicativo que modificaria o estado dos objetos da tela, inclusive os declarados em XML, em tempo de execução.

A vantagem de declarar a IU em XML é separar melhor a apresentação do aplicativo do código que controla seu comportamento. As descrições da IU são externas ao código do aplicativo, ou seja, é possível modificá-las ou adaptá-las sem modificar o código-fonte e recompilar. Por exemplo, é possível criar layouts XML para diferentes orientações de tela, diferentes tamanhos de tela de dispositivos e diferentes idiomas. Além disso, a declaração de layout em XML facilita a exibição da estrutura da sua IU, o que facilita a depuração de problemas. Assim sendo, este documento se concentrará em ensinar a declarar o layout em XML. Se você estiver interessado em instanciar objetos View em tempo de execução, consulte as referências das classes ViewGroup e View.

Em geral, o vocabulário XML para declarar elementos da IU segue rigorosamente a estrutura e a atribuição de nome às classes e aos métodos, em que os nomes de elementos correspondem a nomes de classe e nomes de atributos correspondem a métodos. Na verdade, a correspondência normalmente é tão direta que é possível supor qual atributo XML corresponde a determinado método de classe ou supor qual classe corresponde a certo elemento XML. Contudo, observe que nem todo vocabulário é idêntico. Em alguns casos, há pequenas diferenças de nome. Por exemplo, o elemento EditText tem um atributo text que corresponde a EditText.setText().

Dica: Veja os diferentes tipos de layout em Objetos de layout comuns.

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, eis um layout XML que usa um LinearLayout vertical para conter uma 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 retorno de chamada 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 Atividade desta forma:

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

O método de retorno de chamada onCreate() na Atividade é chamado pela estrutura do Android quando ela é inicializada (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.

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 o ID 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 pela estrutura 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 espaço de nome do pacote android da seguinte maneira:

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

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

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

  1. Definir uma vista/widget no arquivo de layout e atribuir um ID exclusivo a ele/ela:
    <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, crie uma instância do objeto de exibição e capture-a do layout (normalmente no método onCreate()):
    Button myButton = (Button) findViewById(R.id.my_button);
    

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

Os IDs não precisam ser exclusivo 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 a 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 exibição filha, conforme necessário para o grupo de exibições. Como se pode ver na figura 1, um grupo de exibições pais define parâmetros de layout para cada exibição filha (incluindo o grupo de exibições filhas).

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

Observe que 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 exibições contêm largura e altura (layout_width e layout_height) e cada exibiçã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 se redimensionar de acordo com as medidas exigidas pelo conteúdo.
  • match_parent instrui a exibição a assumir o maior tamanho permitido pelo grupo de exibiçõ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 geometria de uma exibição de um retângulo. As exibições têm uma localização, expressa como um par de coordenadas esquerda e topo e 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 exibição chamando os métodos getLeft() e getTop(). O primeiro retorna a coordenada esquerda, ou X, do retângulo que representa a exibição. O último retorna a coordenada topo, ou Y, do retângulo que representa a exibição. Esses métodos retornam a localização da exibição em relação ao pai correspondente. Por exemplo, quando getLeft() retorna 20, significa que a exibição se localiza 20 pixels à direita da borda esquerda do seu pai direto.

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

Tamanho, preenchimento e margens

O tamanho de uma exibição é expresso por largura e altura. As exibiçõ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 exibição terá dentro da exibição pai. As dimensões medidas podem ser obtidas 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 exibição na tela, em tempo de desenho e após o layout. Esses valores podem diferir da largura e da altura medidas. Os valores de largura e altura podem ser obtidos chamando getWidth() e getHeight().

Para medir as dimensões, a exibição leva em conta o preenchimento. O preenchimento é expresso em pixels para a esquerda, a direita e as partes de cima e de baixo da exibição. O preenchimento pode ser usado para compensar o conteúdo da exibição por um número específico de pixels. Por exemplo, um preenchimento à esquerda de 2 empurrará o conteúdo da exibiçã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 exibição possa definir um preenchimento, ela não fornece nenhuma compatibilidade com margens. No entanto, os grupos de exibições oferecem essa compatibilidade. Consulte ViewGroup e ViewGroup.MarginLayoutParams para ver mais informações.

Para obter 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 exibiçõ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 obter 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 exibições grande é melhor do que uma hierarquia de exibições profunda).

Layout linear

Layout que organiza os filhos em uma única linha horizontal ou vertical. Ele cria uma barra de rolagem se o comprimento da janela excede 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 no topo do pai).

Exibiçã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 exibições em tempo de execução. Uma subclasse da classe AdapterView usa um Adapter para agrupar dados ao seu 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 exibição que pode ser adicionada ao layout do AdapterView.

Alguns layouts comuns retornados por um adaptador:

Exibição List

Exibe uma lista de rolagem de coluna única.

Exibição Grid

Exibe uma grade de rolagem de colunas e linhas.

Preenchimento da exibiçã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 exibiçõ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 exibiçã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 deseja exibir em uma ListView, inicialize um novo ArrayAdapter usando um construtor para especificar o layout de cada string e a matriz de strings:

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:

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 exibiçã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 exibição que deseja 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 que colunas no Cursor devem ser inseridas em determinadas exibiçõ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 exibições correspondentes em que cada coluna deve ser colocada:

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:

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 exibição de cada linha no Cursor usando o layout fornecido por meio da inserção de cada item de fromColumns na exibição toViews correspondente.

.

Se, durante o curso de vida do aplicativo, você mudar os dados subjacentes lidos pelo adaptador, chame notifyDataSetChanged(). Isso notificará à exibiçã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:

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

listView.setOnItemClickListener(mMessageClickedHandler);