Tricki układu: Łączenie układów
W poprzednich artykułach pokazano, jak używać znacznika <include />
w układach XML, aby ponownie wykorzystać i udostępnić kod układu. Ten artykuł wyjaśnia znacznik <merge />
i jak uzupełnia on znacznik <include />
.
Znacznik <merge />
został stworzony w celu optymalizacji układów Androida przez zmniejszenie liczby poziomów w drzewach widoków. Łatwiej jest zrozumieć problem, który rozwiązuje ten znacznik, patrząc na przykład. Poniższy układ XML deklaruje układ, który wyświetla obraz z tytułem na wierzchu. Struktura jest dość prosta; FrameLayout
jest użyty do ułożenia TextView
na wierzchu 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>
Ten układ renderuje się ładnie i nic nie wydaje się z nim nie tak:
Rzeczy stają się bardziej interesujące, gdy sprawdzisz wynik za pomocą HierarchyViewera. Jeśli przyjrzysz się dokładnie wynikowemu drzewu, zauważysz, że FrameLayout
zdefiniowany w naszym pliku XML (zaznaczony na niebiesko poniżej) jest jedynym dzieckiem innego FrameLayout
:
Ponieważ nasz FrameLayout
ma te same wymiary co jego rodzic, dzięki użyciu ograniczeń fill_parent
, i nie definiuje żadnego tła, dodatkowego paddingu ani grawitacji, jest całkowicie bezużyteczny. Zwiększyliśmy tylko złożoność UI bez większego powodu. Ale jak moglibyśmy się pozbyć tego FrameLayout
? Przecież dokumenty XML wymagają znacznika aroot, a znaczniki w układach XML zawsze reprezentują instancje widoku.
Tutaj właśnie przydaje się znacznik <merge />
. Kiedy LayoutInflater
napotyka ten znacznik, pomija go i dodaje dzieci <merge />
do znacznika <merge />
rodzica. Mylisz się? Przepiszmy nasz poprzedni układ XML, zastępując znacznik FrameLayout
znacznikiem <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>
W tej nowej wersji zarówno znacznik TextView
, jak i znacznikImageView
zostaną dodane bezpośrednio do znacznika najwyższego poziomuFrameLayout
. Wynik będzie wizualnie taki sam, ale hierarchia widoków będzie prostsza:
Oczywiście, użycie <merge />
działa w tym przypadku, ponieważ rodzicem widoku zawartości aktywności jest zawsze FrameLayout
. Nie mógłbyś zastosować tej sztuczki, gdyby twój layout używał znacznika LinearLayout
jako znacznika głównego, na przykład. Znacznik <merge />
może być jednak użyteczny w innych sytuacjach. Na przykład, działa doskonale w połączeniu z tagiem<include />
. Możesz również użyć <merge/>
kiedy tworzysz niestandardowy widok złożony. Zobaczmy jak możemy użyć tego znacznika do stworzenia nowego widoku o nazwie OkCancelBar
, który po prostu pokazuje dwa przyciski z konfigurowalnymi etykietami. Możesz również pobrać kompletny kod źródłowy tego przykładu. Oto XML użyty do wyświetlenia tego niestandardowego widoku na górze obrazu:
<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>
Ten nowy układ daje następujący wynik na urządzeniu:
Kod źródłowy OkCancelBar
jest bardzo prosty, ponieważ dwa przyciski są zdefiniowane w zewnętrznym pliku XML, wczytanym za pomocąLayoutInflate
. Jak widać w poniższym snippecie, układ XML R.layout.okcancelbar
jest nadmuchiwany zOkCancelBar
jako rodzicem:
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(); }}
Dwa przyciski są zdefiniowane w następującym układzie XML. Jak widać, używamy znacznika <merge />
, aby dodać dwa przyciski bezpośrednio doOkCancelBar
. Każdy przycisk jest dołączony z tego samego zewnętrznego pliku XMLlayout, aby ułatwić ich utrzymanie; po prostu nadpisujemy ich 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>
Utworzyliśmy elastyczny i łatwy w utrzymaniu widok niestandardowy, który generuje wydajną hierarchię widoków:
Znacznik <merge />
jest niezwykle użyteczny i może zdziałać cuda w twoim kodzie. Jednak cierpi on na kilka ograniczeń:
-
<merge />
może być używany tylko jako znacznik główny układu XML - Podczas nadmuchiwania układu zaczynającego się od
<merge />
, musisz określić rodzicaViewGroup
i musisz ustawićattachToRoot
natrue
(zobacz dokumentację dla metodyinflate(int, android.view.ViewGroup, boolean)
)
.
Leave a Reply