Muitos desenvolvedores iniciantes em Android sentem alguma dificuldade em trabalhar com ViewGroups mais complexos como o RelativeLayout.
Esse curto artigo e tutorial tem o objetivo de simplificar essa tarefa, com apenas três dicas muito simples de seguir.
1. Relative Layout em 3 Lições
Copérnico Vespúcio
http://renascienza.wix.com
2. Introdução
Desde que computadores ganharam telas e programas interfaces gráficas, dar suporte
a tamanhos e resoluções de tela diferentes é um desafio recorrente. No mundo móvel,
com sua infinidade de modelos que variam de acordo com o sempre flutuante gosto
dos consumidores, o tema é ainda mais relevante.
Algumas plataformas simplesmente subvalorizaram o problema e oferecem soluções
ineficientes, outras deixam o problema no colo dos desenvolvedores. Para a sorte de
quem desenvolve para o sistema operacional Android, ela não é uma dessas
plataformas. Usada atualmente em mais de 500 modelos diferentes em todo o mundo,
ela nasceu com a compatibilidade como compromisso.
Neste artigo, eu vou mostrar a melhor e mais simples estratégia para usar uma das
mais poderosas ferramentas de layout que o desenvolvedor Android tem a sua
disposição: O RelativeLayout.
3. Um Problema Inicial
Um problema clássico de layout é o de um formulário arbitrário:
vários campos com tamanhos e proporções diferentes. O designer
deve levar em consideração não apenas a extensão e a disposição
de cada campo, como também a maneira como eles
redimensionam quando a tela muda de tamanho.
Sua primeira tentação é organizar os campos por tamanho de forma
a seguir uma disposição mais ou menos regular? Bem, podem haver
ainda mais razões para não fazer isso:
4. Um Problema Inicial
a) A maior parte dos usuários no mundo lêem formulários da esquerda para a direita, na
ordem em que pensam nos dados. Trocar essa ordem lógica pode fazer com que fiquem
perdidos, aumentando o tempo que eles precisam para se acostumar à sua interface.
b) Por alguma razão, a maioria dos usuários fica mais confortável quando a maior parte da
informação em uma tela fica do lado direito. Se vc tem um componente que atraia mais o
olhar, como uma foto, posicioná-la do lado direito cria uma tela mais agradável.
c) Pode ser que o seu aplicativo esteja reproduzindo uma tela de outro programa ou um
documento impresso com o qual o usuário já esteja acostumado.
6. Abordagem "newbie": ViewGroups aninhados
A primeira abordagem em que poderíamos pensar é aninhar ViewGroups básicos para conseguir
esse efeito, como na listagem que produz a tela a seguir. Não perca muito tempo com ela, porque é
a pior abordagem que vc pode adotar. Ela desperdiça recursos preciosos do dispositivo, porque:
a) Cada nó em uma árvore de layout custa memória: views e view groups são objetos complexos. Do
total de views nesse layout, mais de um terço é usado apenas para arrumar os componentes.
b) Cada View a ser desenhada envolve um ciclo de pré-medição, layout, medição e desenho. Cada
fase desse ciclo em um ViewGroup chama recursivamente o ciclo de todas as views agregadas a
ele. Por isso, view groups aninhados tem péssima performance. E isso consome tempo e bateria.
Esse é o motivo da existência de abordagens como RelativeLayout . Com ele, vc pode criar qualquer
disposição que quiser, usando um único ViewGroup.
8. Primeira tentativa de otimização
A ideia por trás do RelativeLayout é aparentemente muito simples. Direto das
páginas da documentação:
"A posição de cada view pode ser especificada como a relação entre as irmãs
(tais como para a esquerda, de ou abaixo de outra view) ou em posições
relativas ao container pai"
Então cada componente é fixado a partir da posição de pelo menos um outro.
Fácil. Ok, vamos fazer isso. A regra parece simples: vc vai adicionando os
componentes e amarrando os próximos no predecessor mais adequado.
10. Primeira tentativa de otimização
??
Parece um pouco decepcionante,
não é exatamente o que vc
imaginou. Alguns componentes
estão ocultos, outros mal
dimensionados e, caso vc precise
mudar a posição de um
componente chave, o resto da sua
UI vai cair como um castelo de
cartas!
Esse é o ponto em que a maioria
desiste após algumas tentativas e
faz uma regressão para o layout
aninhado anterior.
Não vamos fazer isso. Somos corajosos. Somos ninjas!
Vamos às nossas 3 lições:
11. Lição 1: Conheça sua arma, antes de atirar.
A parte não óbvia por trás de qualquer ViewGroup, incluindo o RelativeLayout reside nas respostas de
duas perguntas:
a) Como ele posiciona os componentes?
Assumir que esse ViewGroup posiciona cada componente em relação a outros ou ao container-pai é
correto, mas limitado. Vc precisa lembrar que uma tela tem um eixo cartesiano com uma coordenada
horizontal (x) e vertical (y). Sendo assim, para ancorar direito a posição de um componente, vc precisa
de ambas as coordenadas.
No Relative Layout, sempre ancore um componente tanto na vertical quanto na horizontal
Importante: Não são permitidas "referências cruzadas", ou seja: se o componente A usa o componente
B como apoio, o componente B não pode usar o componente A como apoio.
b) Como ele dimensiona os componentes?
Os mais atentos perceberão que RelativeLayout possui constraints de alinhamento (borda superior,
borda inferior, esquerda e direita) que podem posicionar suas bordas em relação às bordas de outros
componentes ou do container-pai. A posição dessas bordas determina o tamanho do componente na
tela e sobrescreve qualquer medida especificada.
No entanto, quando há ausência de constraints que determinem a largura e/ou a altura do componente,
a medida especificada é utilizada.
12. Lição 2: Use apoios confiáveis.
Sabendo agora como realmente o RelativeLayout funciona, podemos assumir que a
estratégia de fazer os componentes de uma tela dependerem inteiramente uns dos
outros faz tanto sentido quanto a de amigos bêbados saindo de uma festa tentanto se
apoiar uns nos outros.
Ao invés disso, use os pontos de referência mais confiáveis: os do container-pai. Você
tem a sua disposição quatro bordas e o centro. O container-pai nunca muda na
manutenção - porque vc não precisa especificar um id para usá-lo - e é esperado que
ele sempre tenha as proporções corretas em relação a tela em que está inserido.
Em primeiro lugar, sempre que houver escolha entre usar um outro componente ou o
container para posição ou alinhamento, escolha o container.
Em segundo lugar, sempre que houver opção para usar um componente de tamanho
e/ou posição fixos como referência, prefira essa opção.
Vejamos como fica isso, na prática, reescrevendo nosso exemplo
13. A primeira linha
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/product"
android:hint="Product"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/ref"
android:minWidth="180dp"/>
Alinhamos todos os componentes no topo do
container. O primeiro componente da linha é
ancorado na borda esquerda e o último na borda
direita. Isso dá a ancoragem sólida que já discutimos.
<EditText
android:layout_width="64dp"
android:layout_height="wrap_content"
android:id="@+id/ref"
android:hint="REF"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/retailer"
/>
Vimos em nosso rascunho que na primeira linha a
caixa "Product" é a única com permissão de
redimensionar na largura. Para conseguir isso
fixamos o tamanho dos dois outros componentes da
linha e deixamos o tamanho de "Product" para variar
dentro de um limite mínimo especificado até o
máximo permitido.
<EditText
android:layout_width="180dp"
android:layout_height="wrap_content"
android:id="@+id/retailer"
android:hint="Ret. Code"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
/>
Os tamanhos fixos funcionam porque, na ausência de
outros fatores, a medida especificada é respeitada.
Uma vez em que são os componentes da direita a
terem tamanhos fixos, posicionamos a borda de
"Product" a eles e não o contrário.
14. A segunda linha
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/category"
android:hint="Category"
android:layout_below="@+id/product"
android:layout_alignParentLeft="true"
android:minWidth="200dp"
/>
Olhando em nosso rascunho, vimos que o único componente da linha
que pode variar de tamanho é "Photo".
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/photo"
android:src="@drawable/person"
android:layout_alignParentRight="true"
android:layout_below="@+id/ref"
android:maxWidth="250dp"
android:maxHeight="250dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:minHeight="180dp"
android:minWidth="180dp" />
No entanto o bom senso nos diz que os outros componentes na linha
precisam ter tamanhos mínimos e que o campo "Photo" deveria ter
limites máximos (do contrário escolher uma foto especialmente grande
engoliria o resto da tela).
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/price"
android:hint="USD 0.00"
android:layout_below="@+id/product"
android:layout_toRightOf="@+id/category"
android:layout_toLeftOf="@+id/taxes"
android:minWidth="100dp"
/>
<EditText
android:layout_width="48dp"
android:layout_height="wrap_content"
android:id="@+id/taxes"
android:hint="0%"
android:layout_below="@+id/product"
android:layout_toLeftOf="@+id/photo"
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/amount"
android:hint="0"
android:layout_below="@+id/product"
android:layout_toRightOf="@+id/taxes"
android:layout_toLeftOf="@+id/photo"
android:minWidth="96dp"
/>
/>
Para isso usamos maxWidth, maxHeight e minWidth, minHeight onde
necessário.
Importante: ImageView é um componente um tanto particular. Quando
há conteúdo, define seu tamanho de acordo com este e não com as
medidas especificadas (layout_width, layout_height, maxWidth,
maxHeight, minWidth, minHeight). Para mudar esse comportamento
definimos "android:adjustViewBounds" como "true" e especificamos um
método de redimensionamento do conteúdo.
Uma vez que agora é a direita da linha a parte com a largura variável,
ancoramos os componentes à direita com os da esquerda.
15. A terceira e quarta linhas
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/description"
android:hint="Description"
android:layout_below="@+id/category"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/taxes"
android:layout_alignEnd="@+id/taxes"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/comments"
android:hint="Comments"
android:gravity="top"
android:inputType="textMultiLine"
android:layout_alignBottom="@+id/photo"
android:layout_below="@+id/description"
android:layout_alignParentLeft="true"
android:layout_toRightOf="@id/photo"
android:layout_alignRight="@+id/taxes"
android:layout_alignEnd="@+id/taxes"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Validate"
android:id="@+id/validate"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignRight="@+id/taxes"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:id="@+id/send"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/amount"/>
O mesmo é feito com as linhas de "Description"
e "Comments".
Alinhamos a borda inferior de "Comments" com
a borda inferior de "Photo", de forma coerente.
Finalmente, alinhamos os botões da parte
inferior com o container.
Poderíamos alinhar as bordas inferiores de
"Comments" e "Photo" com o topo dos botões,
mas não queremos deformar "Photo" caso a
altura do container fique maior do que
gostaríamos.
16. Resultado parcial
No fim, temos um resultado
quase sem defeitos.
Uma última coisa ainda
incômoda: as larguras dos botões
inferiores não são proporcionais
entre si.
Isso acontece porque estão
baseadas em referências acima e
não podemos basear um botão no
outro porque nenhum deles tem
largura fixa (eis a regra de que
dois bêbados não podem se
apoiar um no outro)...
O que fazer?
17. Lição 3: Se as peças não se prendem muito bem, use cola!
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Validate"
android:id="@+id/validate"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="@+id/glue"/>
<View
android:layout_width="8dp"
android:layout_height="48dp"
android:id="@+id/glue"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:id="@+id/send"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_toRightOf="@+id/glue"/>
O problema dos botões na parte de baixo é que
eles precisam de algo em que se apoiar, um
terceiro elemento. Eu o chamo de "componente
cola".
Adicionamos uma view extra e a ancoramos
com a parte inferior do container e com o seu
centro horizontal. Agora os botões possuem
um ponto fixo no qual se ancorar!
18. Resultado final
Saldo final da modificação em
relação ao primeiro layout
aninhado: apenas uma view
comum extra em troca de 5
ViewGroups.
Conclusão: O ViewGroup
RelativeLayout, usado
corretamente, é o um dos mais
poderosos e versáteis recursos
que o design de interface pode
lançar mão. Agora, um pouco de
prática é tudo o que você precisa
para se tornar um mestre, como
em tudo o mais.
Boa jornada!