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:

A FrameLayout jest użyty do nałożenia tytułu na obraz

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:

Układ z tylko jednym dzieckiem o tych samych wymiarach może zostać usunięty

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:

Optymalna hierarchia widoków przy użyciu znacznika łączenia

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:

Tworzenie niestandardowego widoku za pomocą tagu merge

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:

Wynikowa hierarchia jest prosta i wydajna

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ć rodzica ViewGroup i musisz ustawić attachToRoot na true (zobacz dokumentację dla metody inflate(int, android.view.ViewGroup, boolean))

.

Leave a Reply