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:

A FrameLayout est utilisé pour superposer un titre au dessus d'une image

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:

Un layout avec un seul enfant de mêmes dimensions peut être supprimé

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:

Hiérarchie des vues optimisée en utilisant la balise de fusion

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:

Création d'une vue personnalisée avec la balise merge

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 hiérarchie résultante est simple et 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 parent ViewGroup et vous devez définir attachToRoot à true (voir la documentation pour la méthode inflate(int, android.view.ViewGroup, boolean))

.

Leave a Reply