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

TextInputLayout, czyli animowany EditText

To już kolejny wpis o bibliotece:
Android Design Support Library
Dzisiaj chciałem skupić się na bardzo ładnie animowanym polu typu EditText, a mianowicie:

Prawda, że ładne? Zwłaszcza, że możemy dowolnie dobrać sobie te kolory. Ale do kodu. Aby uzyskać taki efekt potrzebujemy obudować nasze pole EditText czymś takim:

<android.support.design.widget.TextInputLayout
        android:layout_width=”match_parent”
        android:layout_height=”wrap_content”
        android:theme=”@style/TextInputTheme”
        android:id=”@+id/usernameTIL” >

<EditText
            android:id=”@+id/usernameET”
            android:layout_width=”match_parent”
            android:layout_height=”wrap_content”
            android:textSize=”23sp”
            android:hint=”@string/username” />

</android.support.design.widget.TextInputLayout>

Oczywiście, o czym wspominałem w poprzednim wpisie, musimy dodać bibliotekę do gradle:

compile 'com.android.support:design:22.2.0′

Kod który wkleiłem wygląda jakby opisywał się sam, więc nie będę się nad nim rozwodził, ale pokażę w jaki sposób ustawić sobie kolory na takie, które nas interesują i pasują do projektu. Wszystko kryje w pliku styles.xml, dodajemy tam:

<style name=”TextInputTheme” parent=”TextAppearance.AppCompat”>
        <item name=”android:textColorHint”>@color/primary</item>
        <item name=”colorAccent”>@color/accent</item>
        <item name=”colorControlNormal”>@color/controlNormal</item>
        <item name=”colorControlActivated”>@color/controlActivated</item>
    </style>

Tutaj również wszystko jest samo-opisowe, a jeśli nie jest, to zapraszam do dokumentacji, bądź samodzielnych prób. Te mają to do siebie, że czasem możemy zmienić inny element niż chcieliśmy, ale w efekcie osiągnąć coś innego, ciekawszego niż planowaliśmy. Przynajmniej mi, czasem się tak zdarza.

Ostatnią rzeczą o której chciałem powiedzieć jest efekt uzyskany po wykonaniu po stronie javy tego kodu:

setErrorEnabled(true);
setError(getString(R.string.error_message));

Dzięki temu, możemy ustalić, aby wymagane było uzupełnienie naszego pola. W przypadku jeśli ktoś przejdzie do następnego elementu, zostanie w polu EditText wyświetlona informacja zawarta w stringu error_message. Tutaj również pojawia się ładna animacja, zresztą, sprawdźcie sami.

Pozdrawiam!