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:
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
:
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:
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:
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:
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 genitoreViewGroup
e si deve impostareattachToRoot
atrue
(vedere la documentazione del metodoinflate(int, android.view.ViewGroup, boolean)
)
Leave a Reply