Traces de mise en page : Fusionner les mises en page
Les articles vous ont montré comment utiliser la balise <include />
dans les mises en page XML, pour réutiliser et partager votre code de mise en page. Cet article explique la balise <merge />
et comment elle complète la balise <include />
.
La balise <merge />
a été créée dans le but d’optimiser les mises en page Android en réduisant le nombre de niveaux dans les arbres de visualisation. Il est plus facile de comprendre le problème que cette balise résout en regardant un exemple. La mise en page XML suivante déclare une mise en page qui affiche une image avec son titre par-dessus. La structure est assez simple ; un FrameLayout
est utilisé pour empiler un TextView
au dessus d’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>
Cette mise en page rend bien et rien ne semble clocher:
Les choses deviennent plus intéressantes lorsque vous inspectez le résultat avec HierarchyViewer. Si vous regardez attentivement l’arbre résultant, vous remarquerez que le FrameLayout
défini dans notre fichier XML (surligné en bleu ci-dessous) est le seul enfant d’un autre FrameLayout
:
Puisque notre FrameLayout
a la même dimension que son parent, en vertu de l’utilisation des contraintes fill_parent
, et ne définit aucun arrière-plan, aucun padding supplémentaire ou une gravité, il est totalement inutile. Nous avons seulement rendu l’interface utilisateur plus complexe sans raison valable. Mais comment pourrions-nous nous débarrasser de cette FrameLayout
? Après tout, les documents XML nécessitent une balise racine et les balises dans les mises en page XML représentent toujours des instances de vue.
C’est là que la balise <merge />
est utile. Lorsque leLayoutInflater
rencontre cette balise, il la saute et ajoute les <merge />
enfants au <merge />
parent. Vous êtes confus ? Réécrivons notre mise en page XML précédente en remplaçant les FrameLayout
par des <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>
Avec cette nouvelle version, les TextView
et lesImageView
seront ajoutés directement au niveau supérieurFrameLayout
. Le résultat sera visuellement le même mais la hiérarchie des vues sera plus simple:
Evidemment, l’utilisation de <merge />
fonctionne dans ce cas parce que le parent de la vue de contenu d’une activité est toujours un FrameLayout
. Vous ne pourriez pas appliquer cette astuce si votre mise en page utilisait une LinearLayout
comme balise racine par exemple. La balise <merge />
peut cependant être utile dans d’autres situations. Par exemple, elle fonctionne parfaitement lorsqu’elle est combinée avec la balise<include />
. Vous pouvez également utiliser <merge/>
lorsque vous créez une vue composite personnalisée. Voyons comment nous pouvons utiliser cette balise pour créer une nouvelle vue appelée OkCancelBar
qui affiche simplement deux boutons avec des étiquettes personnalisables. Vous pouvez également télécharger le code source complet de cet exemple. Voici le XML utilisé pour afficher cette vue personnalisée au-dessus d’une image:
<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>
Cette nouvelle disposition produit le résultat suivant sur un appareil:
Le code source de OkCancelBar
est très simple car les deux boutons sont définis dans un fichier XML externe, chargé à l’aide d’uneLayoutInflate
. Comme vous pouvez le voir dans le snippet suivant, le XMLlayout R.layout.okcancelbar
est gonflé avec leOkCancelBar
comme parent:
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(); }}
Les deux boutons sont définis dans le layout XML suivant. Comme vous pouvez le voir, nous utilisons la balise <merge />
pour ajouter les deux boutons directement à la OkCancelBar
. Chaque bouton est inclus à partir du même fichier XMLlayout externe pour les rendre plus faciles à maintenir ; nous surchargeons simplement leur 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>
Nous avons créé une vue personnalisée flexible et facile à maintenir qui génère une hiérarchie de vue efficace:
La balise <merge />
est extrêmement utile et peut faire des merveilles dans votre code. Cependant, elle souffre de quelques limitations:
-
<merge />
ne peut être utilisée que comme balise racine d’une mise en page XML - Lorsque vous gonflez une mise en page commençant par une
<merge />
, vous devez spécifier un parentViewGroup
et vous devez définirattachToRoot
àtrue
(voir la documentation pour la méthodeinflate(int, android.view.ViewGroup, boolean)
)
.
Leave a Reply