#2 Nie dla metody Copiego-Paste’a, czyli ponowne użycie layoutów w Androidzie

Możliwość ponownego użycia fragmentu kodu, funkcjonalności jest jedną z wytycznych tworzenia dobrego, obiektowego oprogramowania. Znamy klasy, z których możemy dziedziczyć (btw, wiecie jaki jest najlepszy obiektowy sposób zdobycia fortuny? Dziedziczenie :D), znamy interfejsy, przeciążanie itd. Logicznie podobne podejście możemy zastosować tworząc interfejsy użytkownika w androidzie.

Zaczynamy od najmniejszego elementu który chcemy wykorzystać, czyli w przypadku mojej aplikacji, był to ogólny typ: View (przynajmniej na razie, finalnie pewnie zostanie zastąpiony jakimś ImageView). Dla tego typu View, który w aplikacji będzie symbolizował pojedynczą belkę tworzymy odpowiedni styl (styles.xml), u mnie wyglądało to tak:

    <style name=”BricksStyle” parent=”AppTheme”>
        <item name=”android:layout_width”>match_parent</item>
        <item name=”android:layout_height”>wrap_content</item>
        <item name=”android:layout_weight”>1</item>
        <item name=”android:background”>@color/colorAccent</item>
        <item name=”android:layout_margin”>5dp</item>
        <item name=”android:visibility”>invisible</item>
    </style>

Warto korzystać ze stylów nawet kiedy mamy mieć dwa takie same przyciski w aplikacji, dzięki temu zmiana jednego parametru, będzie następowała w jednym miejscu. Ja będę miał naprawdę sporo tych obiektów, dlatego chociażby zmiana parametru visibility w jednym miejscu zamiast stu, pozwala mi podejrzeć aplikację na podglądzie w android studio. Przed zbudowaniem aplikacji też wystarczy zmiana tylko w tym jednym miejscu, żeby zachować jej logikę działania. Gdybym chciał zmienić kolor belek, to znowu tylko jedna linia kodu. A oto jak wykorzystałem ten styl:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
    android:id=”@+id/container”
    android:layout_width=”match_parent”
    android:layout_height=”wrap_content”
    android:orientation=”vertical”>

    <View
        android:id=”@+id/brick10V”
        style=”@style/BricksStyle” />

    <View
        android:id=”@+id/brick9V”
        style=”@style/BricksStyle” />

    <View
        android:id=”@+id/brick8V”
        style=”@style/BricksStyle” />

Jak widać, zamiast do każdego obiektu View wklepywać to samo, wpisuję tylko jedną linię kodu.

Ok, a jak tego użyć we właściwym activiti layoucie? Korzystamy z dyrektywy include, która może przyjmować parametry takie jakie dostarcza nam właściwy layout. Oto jakie efekty udało mi się osiągnąć:


A oto jak wygląda fragment layoutu:

    <include
        android:layout_width=”wrap_content”
        android:layout_height=”wrap_content”
        layout=”@layout/stack_bricks_layout”
        android:layout_above=”@+id/lineTV”
        android:layout_alignParentStart=”true”
        android:layout_marginLeft=”15dp”
        android:layout_marginRight=”15dp”
        android:layout_toStartOf=”@+id/ITV”
        android:layout_below=”@+id/plusLeftBtn”
        android:id=”@+id/includeLeftStack” />

    <include
        android:layout_width=”wrap_content”
        android:layout_height=”wrap_content”
        layout=”@layout/stack_bricks_layout”
        android:layout_above=”@+id/lineTV”
        android:layout_alignParentEnd=”true”
        android:layout_marginLeft=”15dp”
        android:layout_marginRight=”15dp”
        android:layout_toEndOf=”@+id/ITV”
        android:id=”@+id/includeRightStack”

        android:layout_below=”@+id/minusRightBtn” />


Jak widać, zawiera ona dwie dyrektywy include, które działają z tymi przyciskami do góry (plus i minus z prawej i lewej strony). I tutaj dochodzimy do sedna problemu. Mamy po dwa takie same identyfikatory na jednym activiti-layoucie.  Tzn np: @+id/brick1V po prawej i po lewej stronie, w jaki sposób przyciski odróżniają, do którego layoutu mają dodawać i odejmować? Działa to w ten sposób, że łączymy się z każdym z tych layoutów dzięki identyfikatorowi dyrektywy include. Znowu będę posiłkował się kodem:
    public View[] getBricksStack(View container)
    {
        View bricks1 = container.findViewById(R.id.brick1V);
        View bricks10 = container.findViewById(R.id.brick10V);
        return new View[]{null, bricks1, bricks2, bricks3, bricks4, bricks5, bricks6,
                bricks7, bricks8, bricks9, bricks10};
    }


    public void refreshView(int counter, View[] bricksArray)
    {
        for(int i = 1; i<=10; i++)
        {
            if(i<=counter)
                bricksArray[i].setVisibility(View.VISIBLE);
            else
                bricksArray[i].setVisibility(View.INVISIBLE);
        }
    }

    private View[] bricksLeftArray;
    private View[] bricksRightArray;
    StackBricksViewGroup stackBricksViewGroup = new StackBricksViewGroup();
        View stackBricksContainerLeft = findViewById(R.id.includeLeftStack);
        View stackBricksContainerRight = findViewById(R.id.includeRightStack);
        bricksLeftArray = stackBricksViewGroup.getBricksStack(stackBricksContainerLeft);
        bricksRightArray = stackBricksViewGroup.getBricksStack(stackBricksContainerRight);

Zastosowanie tego typu podejścia znacznie ogranicza potrzeby kopiowania kodu, możemy stworzyć wielopoziomowy layout i wykorzystywać go w naprawdę fajny sposób, dzięki czemu zmian będziemy dokonywali zawsze tylko w jednym miejscu.

Na ten moment zrezygnowałem z korzystania z fragmentów w aplikacji, w następnym wpisie spróbuję pokazać jak poprawić animację pojawiania się naszego obiektu, jeszcze nigdy tego nie robiłem 😉

PS. Pozwolę sobie na pochwalenie się statystykami od kiedy wystartowałem w tym konkursie 😉

Może w dobie internetu, gdzie zdjęcie z kotem zdobywa milion odsłon to nie dużo, nie mniej dla mnie, to aż 100 osób które interesowało to co mam zaoferowania. Nabrałem ochoty stworzyć post który będzie miał o wiele lepszą jakość, niż zwykła notka. Chcę to robić lepiej, ponieważ ktoś chce to czytać. Dzięki! 😉
Pozdrawiam!