Layout trucs: Layouts samenvoegen

De artikelen lieten u zien hoe u de <include /> tag in XML layouts kunt gebruiken, om uw lay-out code te hergebruiken en te delen. Dit artikel legt de <merge />-tag uit en hoe deze de <include />-tag aanvult.

De <merge />-tag is in het leven geroepen om Android lay-outs te optimaliseren door het aantal niveaus in weergavebomen te verminderen. Het is eenvoudiger om het probleem dat deze tag oplost te begrijpen door naar een voorbeeld te kijken. De volgende XML layout declareert een layout die een afbeelding toont met de titel erbovenop. De structuur is vrij eenvoudig; een FrameLayout wordt gebruikt om een TextView bovenop een ImageView te stapelen:

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

Deze layout rendert mooi en er lijkt niets mis mee:

Een FrameLayout wordt gebruikt om een titel bovenop een afbeelding te plaatsen

Het wordt interessanter als je het resultaat met HierarchyViewer bekijkt. Als u goed naar de resulterende boom kijkt, zult u zien dat deFrameLayout die in ons XML-bestand is gedefinieerd (hieronder in blauw gemarkeerd) het enige kind is van een andere FrameLayout:

Een lay-out met slechts één kind met dezelfde afmetingen kan worden verwijderd

Omdat onze FrameLayout dezelfde afmetingen heeft als zijn ouder, dankzij het gebruik van de fill_parent-restricties, en geen achtergrond, extra opvulling of een zwaartekracht definieert, is hij volkomen nutteloos. We hebben alleen de UI complexer gemaakt zonder goede reden. Maar hoe kunnen we van deze FrameLayout afkomen? Tenslotte hebben XML documenten een root tag nodig en tags in XML layouts vertegenwoordigen altijd view instances.

Daar komt de <merge /> tag van pas. Als deLayoutInflater deze tag tegenkomt, slaat hij hem over en voegt de <merge />-kinderen toe aan de <merge />-ouder. In de war? Laten we onze vorige XML layout herschrijven door deFrameLayout te vervangen door <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>

Met deze nieuwe versie worden zowel de TextView als deImageView direct toegevoegd aan de top-levelFrameLayout. Het resultaat zal visueel hetzelfde zijn, maar de viewhiërarchie is eenvoudiger:

Geoptimaliseerde viewhiërarchie met behulp van de merge tag

Het is duidelijk dat het gebruik van <merge /> in dit geval werkt, omdat de ouder van een inhoudsweergave van een activiteit altijd een FrameLayout is. U zou deze truc niet kunnen toepassen als uw layout bijvoorbeeld een LinearLayout als root-tag zou gebruiken. De <merge /> kan echter ook in andere situaties nuttig zijn. Hij werkt bijvoorbeeld perfect in combinatie met de tag<include />. U kunt <merge/> ook gebruiken wanneer u een aangepaste samengestelde view maakt. Laten we eens kijken hoe we deze tag kunnen gebruiken om een nieuwe view te maken met de naam OkCancelBar die eenvoudigweg twee knoppen laat zien met aanpasbare labels. Je kunt ook de complete broncode van dit voorbeeld downloaden. Dit is de XML die wordt gebruikt om deze aangepaste weergave bovenop een afbeelding weer te geven:

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

Deze nieuwe opmaak levert het volgende resultaat op een apparaat:

Een aangepaste weergave maken met de tag merge

De broncode van OkCancelBar is heel eenvoudig omdat de twee knoppen zijn gedefinieerd in een extern XML-bestand, dat wordt geladen met eenLayoutInflate. Zoals u in het volgende fragment kunt zien, wordt de XML-layout R.layout.okcancelbar opgeblazen met deOkCancelBar als ouder:

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

De twee knoppen zijn gedefinieerd in de volgende XML-layout. Zoals u kunt zien, gebruiken we de <merge /> tag om de twee knoppen direct aan deOkCancelBar toe te voegen. Elke knop is opgenomen in hetzelfde externe XMLlayout-bestand om ze gemakkelijker te kunnen onderhouden; we overschrijven gewoon hun 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>

We hebben een flexibele en gemakkelijk te onderhouden aangepaste view gemaakt die een efficiënte view-hiërarchie genereert:

De resulterende hiërarchie is eenvoudig en efficiënt

De <merge /> tag is uiterst nuttig en kan wonderen doen in uw code. Hij heeft echter te lijden onder een paar beperkingen:

  • <merge /> kan alleen worden gebruikt als de root-tag van een XML-lay-out
  • Wanneer u een lay-out opblaast die begint met een <merge />, moet u een parent ViewGroup opgeven en moet u attachToRoot instellen op true (zie de documentatie voor de methode inflate(int, android.view.ViewGroup, boolean))

Leave a Reply