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:

Um FrameLayout é usado para sobrepor um título em cima de uma imagem

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:

Um layout com apenas um filho das mesmas dimensões pode ser removido

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:

Hierarquia de visualização otimizada usando a tag merge

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:

Criando uma vista personalizada com a tag merge

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 hierarquia resultante é simples e 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 parente ViewGroup e você deve definir attachToRoot para true (veja a documentação para inflate(int, android.view.ViewGroup, boolean) método)

Leave a Reply