Layout Tricks: Fundindo Layouts
Os artigos mostraram como usar a tag <include />
em layouts XML, rasgar e compartilhar seu código de layout. Este artigo explica a tag <merge />
e como complementa a tag <include />
tag.
A tag <merge />
foi criada com o propósito de otimizar os layouts do Android reduzindo o número de níveis nas árvores de visualização. É mais fácil entender o problema que esta tag resolve, olhando para um exemplo. O seguinte layout XML declara um layout que mostra uma imagem com seu título em cima dela. A estrutura é bastante simples; um FrameLayout
é usado para empilhar um TextView
em cima de um ImageView
:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="center" android:src="@drawable/golden_gate" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dip" android:layout_gravity="center_horizontal|bottom" android:padding="12dip" android:background="#AA000000" android:textColor="#ffffffff" android:text="Golden Gate" /></FrameLayout>
Este layout torna-o agradável e nada parece errado com ele:
As coisas ficam mais interessantes quando você inspeciona o resultado com o HierarchyViewer. Se você olhar atentamente para a árvore resultante, você notará que o FrameLayout
definido em nosso arquivo XML (destacado em azul abaixo) é o único filho de outro FrameLayout
:
Desde que nosso FrameLayout
tem a mesma dimensão de seu pai, pela virtude de usar as restrições fill_parent
, e não define qualquer fundo, preenchimento extra ou uma gravidade, ele é totalmente inútil. Nós só tornamos a IU mais complexa sem uma boa razão. Mas como poderíamos nos livrar disto FrameLayout
? Afinal de contas, documentos XML requerem tag aroot e tags em layouts XML sempre representam instâncias de visualização.
É aí que a tag <merge />
vem a calhar. Quando a LayoutInflater
encontra esta tag, ela pula e adiciona a <merge />
crianças à <merge />
pai. Confuso? Vamos reescrever nosso layout XML anterior substituindo oFrameLayout
por <merge />
:
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="center" android:src="@drawable/golden_gate" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dip" android:layout_gravity="center_horizontal|bottom" android:padding="12dip" android:background="#AA000000" android:textColor="#ffffffff" android:text="Golden Gate" /></merge>
Com esta nova versão, tanto o TextView
como oImageView
serão adicionados diretamente ao nível superiorFrameLayout
. O resultado será visualmente o mesmo mas a viewhierarchy é mais simples:
Obviamente, usando <merge />
funciona neste caso porque o pai da view de conteúdo de uma atividade é sempre um FrameLayout
. Você não poderia aplicar este truque se seu layout estivesse usando uma LinearLayout
como sua tag raiz, por exemplo. O <merge />
pode ser útil em outras situações, no entanto. Por exemplo, ele funciona perfeitamente quando combinado com a tag<include />
. Você também pode usar <merge/>
quando você cria uma vista composta personalizada. Vamos ver como podemos usar esta tag para criar uma nova vista chamada OkCancelBar
que simplesmente mostra dois botões com etiquetas personalizáveis. Você também pode baixar o código fonte completo deste exemplo. Aqui está o XML usado para mostrar esta vista personalizada no topo de uma imagem:
<merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge"> <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="center" android:src="@drawable/golden_gate" /> <com.example.android.merge.OkCancelBar android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:paddingTop="8dip" android:gravity="center_horizontal" android:background="#AA000000" okCancelBar:okLabel="Save" okCancelBar:cancelLabel="Don't save" /></merge>
Este novo layout produz o seguinte resultado num dispositivo:
O código fonte de OkCancelBar
é muito simples porque os twobuttons são definidos num ficheiro XML externo, carregado usando umLayoutInflate
. Como pode ver no seguinte snippet, o XMLlayout R.layout.okcancelbar
está inflado com oOkCancelBar
como o pai:
public class OkCancelBar extends LinearLayout { public OkCancelBar(Context context, AttributeSet attrs) { super(context, attrs); setOrientation(HORIZONTAL); setGravity(Gravity.CENTER); setWeightSum(1.0f); LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0); String text = array.getString(R.styleable.OkCancelBar_okLabel); if (text == null) text = "Ok"; ((Button) findViewById(R.id.okcancelbar_ok)).setText(text); text = array.getString(R.styleable.OkCancelBar_cancelLabel); if (text == null) text = "Cancel"; ((Button) findViewById(R.id.okcancelbar_cancel)).setText(text); array.recycle(); }}
Os dois botões estão definidos no seguinte layout XML. Como você pode ver, usamos a tag <merge />
para adicionar os dois botões diretamente noOkCancelBar
. Cada botão é incluído a partir do mesmo ficheiro XMLlayout externo para facilitar a sua manutenção; simplesmente substituímos a sua id:
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <include layout="@layout/okcancelbar_button" android:id="@+id/okcancelbar_ok" /> <include layout="@layout/okcancelbar_button" android:id="@+id/okcancelbar_cancel" /></merge>
Criámos uma vista personalizada flexível e fácil de manter que gera uma hierarquia de vistas eficiente:
A tag <merge />
é extremamente útil e pode fazer maravilhas no seu código. No entanto, sofre de algumas limitações:
-
<merge />
só pode ser usado como a tag raiz de um layout XML - Ao inflar um layout começando com um
<merge />
, você deve especificar um parenteViewGroup
e você deve definirattachToRoot
paratrue
(veja a documentação parainflate(int, android.view.ViewGroup, boolean)
método)
Leave a Reply