Trucchi per il layout: Merging Layouts

Gli articoli ti hanno mostrato come usare il tag <include /> nei layout XML, per riutilizzare e condividere il codice del tuo layout. Questo articolo spiega il tag <merge /> e come completa il tag <include />.

Il tag <merge /> è stato creato allo scopo di ottimizzare i layout di Android riducendo il numero di livelli negli alberi delle viste. È più facile capire il problema che questo tag risolve guardando un esempio. Il seguente layout XML dichiara un layout che mostra un’immagine con il suo titolo sopra di essa. La struttura è abbastanza semplice; un FrameLayout viene usato per impilare un TextView sopra un 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>

Questo layout rende bene e non sembra avere nulla di sbagliato:

Un FrameLayout viene usato per sovrapporre un titolo sopra un'immagine

Le cose diventano più interessanti quando si esamina il risultato con HierarchyViewer. Se guardate attentamente l’albero risultante, noterete che il FrameLayout definito nel nostro file XML (evidenziato in blu qui sotto) è l’unico figlio di un altro FrameLayout:

Un layout con un solo figlio delle stesse dimensioni può essere rimosso

Siccome il nostro FrameLayout ha la stessa dimensione del suo genitore, grazie all’uso dei vincoli fill_parent, e non definisce nessuno sfondo, imbottitura extra o gravità, è totalmente inutile. Abbiamo solo reso l’UI più complessa senza una buona ragione. Ma come potremmo liberarci di questo FrameLayout? Dopo tutto, i documenti XML richiedono un tag radice e i tag nei layout XML rappresentano sempre istanze di vista.

Ecco dove il tag <merge /> torna utile. QuandoLayoutInflater incontra questo tag, lo salta e aggiunge i figli <merge /> al padre <merge />. Confusi? Riscriviamo il nostro precedente layout XML sostituendo ilFrameLayout con <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>

Con questa nuova versione, sia il TextView che ilImageView saranno aggiunti direttamente al livello superioreFrameLayout. Il risultato sarà visivamente lo stesso ma la gerarchia delle viste è più semplice:

Gerarchia delle viste ottimizzata usando il tag merge

Ovviamente, l’uso di <merge /> funziona in questo caso perché il padre della vista contenuto di un’attività è sempre un FrameLayout. Non potreste applicare questo trucco se il vostro layout usasse un LinearLayout come tag principale, per esempio. Il <merge /> può però essere utile in altre situazioni. Per esempio, funziona perfettamente se combinato con il tag<include />. Puoi anche usare <merge/> quando crei una vista composita personalizzata. Vediamo come possiamo usare questo tag per creare una nuova vista chiamata OkCancelBar che mostra semplicemente due pulsanti con etichette personalizzabili. Potete anche scaricare il codice sorgente completo di questo esempio. Ecco l’XML usato per visualizzare questa vista personalizzata sopra un’immagine:

<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>

Questo nuovo layout produce il seguente risultato su un dispositivo:

Creazione di una vista personalizzata con il tag merge

Il codice sorgente di OkCancelBar è molto semplice perché i due pulsanti sono definiti in un file XML esterno, caricato usando unLayoutInflate. Come potete vedere nel seguente snippet, l’XMLlayout R.layout.okcancelbar viene gonfiato con ilOkCancelBar come genitore:

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(); }}

I due pulsanti sono definiti nel seguente layout XML. Come potete vedere, usiamo il tag <merge /> per aggiungere i due pulsanti direttamente alOkCancelBar. Ogni pulsante è incluso nello stesso file XMLlayout esterno per renderli più facili da mantenere; semplicemente sovrascriviamo il loro 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>

Abbiamo creato una vista personalizzata flessibile e facile da mantenere che genera un’efficiente gerarchia di viste:

La gerarchia risultante è semplice ed efficiente

Il tag <merge /> è estremamente utile e può fare miracoli nel tuo codice. Tuttavia, soffre di un paio di limitazioni:

  • <merge /> può essere usato solo come tag radice di un layout XML
  • Quando si gonfia un layout che inizia con un <merge />, si deve specificare un genitore ViewGroup e si deve impostare attachToRoot a true (vedere la documentazione del metodo inflate(int, android.view.ViewGroup, boolean))

Leave a Reply