Kurs Android (17)

Autor: Damian Chodorek • Opublikowany: 31 stycznia 2016 • Ostatnia aktualizacja: 20 maja 2016 • Kategoria: android, kursy

Wykorzystanie komponentu Toolbar oraz CoordinatorLayout. Jak animować toolbar podczas przewijania listy?

Dzisiejsza lekcja w pełni będzie opierać się na projekcie i kodzie z lekcji poprzedniej (link).

Toolbar czy ActionBar?

Jakiś czas temu na moim blogu pojawiły się dwie lekcje dotyczące ActionBara, czyli paska znajdującego się w górnej części aplikacji (link 1, link 2). Jaka jest więc różnica pomiędzy ActionBarem a Toolbarem, który dziś omówię?

Otóż Toolbar jest nowym komponentem, który stanowi generalizację ActionBara. Toolbar jest dużo bardziej elastyczny i łatwiej nim manipulować. Stanowi zwykły element hierarchii widoków i może znajdować się w dowolnym miejscu. ActionBar jest pod tym względem znacznie ograniczony.

Jeśli więc potrzebujesz podstawowego zestawu funkcjonalności, który opisałem w podanych lekcjach, skorzystaj z ActionBara. Jeśli jednak chcesz mieć nowoczesny, animowany pasek, to w tym artykule zapoznasz się z Toolbarem.

Zaczynamy

Zakładam, że posiadasz lub przynajmniej umiesz stworzyć kod z poprzedniej lekcji. W pliku res/values/styles.xml zdefiniowany jest główny styl aplikacji. Domyślnie wygenerowany plik, wygląda tak jak poniżej:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>
</resources>

Styl nazywa się AppTheme i dziedziczy po Theme.AppCompat.Light.DarkActionBar. Zmieńmy rodzica tego stylu na Theme.AppCompat.Light.NoActionBar, co sprawi, że domyślny pasek aplikacji zniknie.

Od Androida 5.0 (API Level 21) możliwe jest ustawienie koloru status bara, czyli tego paska, na którym znajduje się godzina, poziom naładowania baterii, połączenie z siecią, itp.

  • 1 – kolor tekstu (textColorPrimary),
  • 2 – kolor status bara (colorPrimaryDark),
  • 3 – kolor Toolbara (colorPrimary).

Zdefiniujmy więc colorPrimaryDark oraz colorPrimary w pliku res/values/color.xml zgodnie ze wskazówkami specyfikacji Material Design.

<resources>
    <color name="ColorPrimary">#03A9F4</color>
    <color name="ColorPrimaryDark">#0288D1</color>
</resources>

Teraz w wykorzystajmy te kolory w pliku styles.xml.

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/ColorPrimary</item>
        <item name="colorPrimaryDark">@color/ColorPrimaryDark</item>
    </style>
</resources>

Toolbar

Toolbarem posługujemy się jak innymi komponentami aplikacji. Ponieważ z założenia Toolbar powinien być w każdej aktywności, zdefiniujmy go w oddzielnym pliku res/layout/tool_bar.xml

<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/ColorPrimary"
    android:elevation="4dp">

</android.support.v7.widget.Toolbar>

Następnie wykorzystajmy layout Toolbara w głównej aktywności activity_main.xml.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FAFAFA">

    <include
        android:id="@+id/tool_bar"
        layout="@layout/tool_bar"></include>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/articles"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/tool_bar" />

</RelativeLayout>

Skoro już podążamy za specyfikacją Material Design, udoskonalmy nasz layout pojedynczego artykułu w pliku article_layout.xml.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="4dp"
    android:layout_marginBottom="4dp"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    card_view:cardBackgroundColor="#FFF"
    card_view:cardCornerRadius="2dp"
    card_view:cardElevation="2dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/article_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Title"
            android:textColor="#DD000000"
            android:paddingTop="24dp"
            android:paddingBottom="16dp"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:textSize="24sp" />

        <TextView
            android:id="@+id/article_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Content"
            android:textColor="#8A000000"
            android:paddingBottom="24dp"
            android:paddingLeft="16dp"
            android:lineSpacingExtra="8dp"
            android:paddingRight="16dp"
            android:textSize="14sp" />
    </LinearLayout>
</android.support.v7.widget.CardView>

Teraz nasza aplikacja powinna wyglądać jak na obrazku poniżej.

Toolbar jest pusty i nie zawiera żadnych elementów. Warto upodobnić go do ActionBara, czyli nadać mu odpowiedni tytuł i zestaw opcji.

public class MainActivity extends AppCompatActivity {

    private Toolbar mToolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // ustawiamy Toolbar jako ActionBar
        mToolbar = (Toolbar) findViewById(R.id.tool_bar);
        setSupportActionBar(mToolbar);

        // to z poprzedniej lekcji:
        setRecyclerView();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // tworzymy menu, dodajemy elementy do ActionBara
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // wykonujemy akcje po kliknięciu na element menu
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void setRecyclerView() {
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.articles);
        recyclerView.setHasFixedSize(true);

        // ustawiamy LayoutManagera
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        // ustawiamy animatora, który odpowiada za animację dodania/usunięcia elementów listy
        recyclerView.setItemAnimator(new DefaultItemAnimator());

        // tworzymy źródło danych - tablicę z artykułami
        ArrayList<Article> articles = new ArrayList<>();
        for (int i = 0; i < 20; ++i)
            articles.add(new Article());

        // tworzymy adapter oraz łączymy go z RecyclerView
        recyclerView.setAdapter(new MyAdapter(articles, recyclerView));
    }
}

Oto jak wygląda aplikacja po zmianach.

CoordinatorLayout

CoordinatorLayout to zaawansowany FrameLayout. Stanowi element nadrzędny (jest rodzicem) i umożliwia koordynowanie zachować swoich potomków. Zmiana stanu jednego dziecka może wywołać zmianę stanu innego dziecka. Oczywiście akcje i reakcje możemy zdefiniować sami, ale część zachowań została zdefiniowana domyślnie. Przykładowo dla Toolbara, który chowa się w przypadku przewijania RecyclerView. Najpierw przedstawię kod activity_main.xml, później wyjaśnię o co w nim chodzi.

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FAFAFA">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <include
            android:id="@+id/tool_bar"
            layout="@layout/tool_bar"></include>

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

    <android.support.v7.widget.RecyclerView
        android:id="@+id/articles"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

Przede wszystkim zmieniliśmy RelativeLayout na android.support.design.widget.CoordinatorLayout. Dodaliśmy także android.support.design.widget.AppBarLayout jako rodzica naszego Toolbara. Jest to rozszerzony LinearLayout zaprojektowany do współpracy z CoordinatorLayotu w taki sposób, aby umożliwić reagowanie na przewijanie.

Elementowi RecyclerView ustawiliśmy app:layout_behavior="@string/appbar_scrolling_view_behavior". Bez tego nasza lista wjeżdżałaby pod Toolbara oraz nie reagowałaby na jego obecność. Wartość wskazuje na android.support.design.widget.AppBarLayout$ScrollingViewBehavior. Jest to statyczna klasa zagnieżdżona w AppBarLayout, która rozszerza android.support.design.widget.CoordinatorLayout.Behavior Klasy typu Behavior umożliwiają CoordinatorLayout wywoływanie odpowiednich interakcji pomiędzy jego potomkami. Nic nie stoi na przeszkodzie, aby tworzyć własne zachowania.

Dodanie więc ScrollingViewBehavior (przypominam: app:layout_behavior="@string/appbar_scrolling_view_behavior") umieści RecyclerView pod AppBarLayout oraz spowoduje aktualizację pozycji, podczas gdy toolbar zostanie schowany.

Musimy dodać jeszcze jeden atrybut do naszego Toolbara.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/ColorPrimary"
    android:elevation="4dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    app:layout_scrollFlags="scroll|enterAlways">

</android.support.v7.widget.Toolbar>

Dodaliśmy więc app:layout_scrollFlags="scroll|enterAlways". Flaga scroll spowoduje, że Toolbar będzie się chował podczas przewijania listy, jednak pojawi się dopiero, gdy wrócimy na jej początek. Jeśli dodamy flagę enterAlways, Toolbar powróci zawsze podczas przewijania listy do góry.

Oto aplikacja ze schowanym Toolbarem (przewijamy listę w dół). Zauważ, że RecyclerView został podniesiony wyżej i przylega do góry ekranu.

Oto aplikacja z pokazującym się Toolbarem (przewijamy w górę).

część 18

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.