打造自己的 APP「冰與火百科」(三):Material Design 控制元件

SouthernBox發表於2019-03-04

自從 Google I/O 2014 釋出 Material Design 到現在,這個設計語言已經相當完善了。Material Design 是我最喜歡的一種設計,在專案中,我會盡可能的使用 Material Design 的控制元件。

下面給大家簡單介紹一下「冰與火百科」裡面涉及到的 Material Design 控制元件。

側滑選單

APP 裡需要一個切換內容以及設定的地方,使用側滑選單是最好的選擇。過去我們都是用 SlidingMenu 這樣的第三方控制元件實現側滑選單,隨著 Design 庫的釋出,我們有了官方的側滑選單。

在 Android Studio 裡新建專案的時候,可以直接選擇帶有側滑選單的模板:

如果沒有選模板,記得在 build.gradle 新增設計庫的依賴:

compile "com.android.support:design:${SUPPORT_LIB_VERSION}"複製程式碼

DrawerLayout

我們看一下首頁的佈局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>複製程式碼

DrawerLayout 包含了主檢視及側滑選單,負責控制側滑的效果。而 NavigationView 就是我們的側滑選單。

如果使用了 ToolBar,並且要實現側滑選單的狀態和 ToolBar 上的按鈕聯動,像這樣:

在初始化的時候加入如下程式碼就可以實現了:

ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
        this,
        drawerLayout,
        toolbar,
        R.string.navigation_drawer_open,
        R.string.navigation_drawer_close);
drawerLayout.addDrawerListener(toggle);
toggle.syncState();複製程式碼

NavigationView

從 NavigationView 的佈局屬性可以看出,它由頭部 headerLayout 及選單列表 menu 兩部分組成。

頭部 headerLayout 就是一個普通佈局,通常用漸變色或者圖片來作背景,在上面顯示程式名或使用者資訊。

下面的選單列表,需要引入一個 menu 配置檔案,這個檔案我是這樣寫的:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_person"
            android:checked="true"
            android:icon="@drawable/menu_person"
            android:title="人物" />
        <item
            android:id="@+id/nav_house"
            android:icon="@drawable/menu_house"
            android:title="家族" />
        <item
            android:id="@+id/nav_history"
            android:icon="@drawable/menu_history"
            android:title="歷史" />
        <item
            android:id="@+id/nav_castles"
            android:icon="@drawable/menu_castle"
            android:title="城堡" />
        <item
            android:id="@+id/nav_night"
            android:checkable="false"
            android:icon="@drawable/menu_bulb"
            android:title="夜間模式"
            app:actionLayout="@layout/menu_switch" />
    </group>

</menu>複製程式碼

前四項是用於切換顯示不同型別的單選按鈕。

最後一項是夜間模式的切換按鈕。它不可點選,並且通過 actionLayout 引入了另一個佈局,佈局裡是一個開關控制元件 SwitchCompat。

Listener

給 NavigationView 設定監聽器,可以實現對側邊欄選單的點選監聽:

navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.nav_person:
                //...
                break;
            case R.id.nav_night:
                boolean isChecked = switchCompat.isChecked();
                switchCompat.setChecked(!isChecked);
                break;
        }
    return true;
    }
});複製程式碼

順便說一下這裡 switchCompat 的獲取:

Menu menu = navigationView.getMenu();
MenuItem nightItem = menu.findItem(R.id.nav_night);
View nightView = MenuItemCompat.getActionView(nightItem);
SwitchCompat switchCompat = (SwitchCompat) nightView.findViewById(R.id.switch_compat);複製程式碼

側滑選單相關程式碼基本就這些了,看一下最終的實現效果:

標籤選項卡

過去要實現頂部標籤欄,也是隻能依賴第三方庫,現在有了 TabLayout,可以很簡單就實現這個效果。

佈局如下:

<android.support.design.widget.TabLayout
    android:id="@+id/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:tabMode="scrollable"
    app:theme="@style/ThemeOverlay.AppCompat.Dark" />複製程式碼

設定 tabMode 為 scrollable,讓標籤欄寬度超過佔用的寬度時可以滾動,不然會將所有標籤壓縮在佔用的寬度裡。

我們知道選項卡是要和 ViewPager 搭配使用的,在初始化時加一句讓它們關聯起來就行了:

tabLayout.setupWithViewPager(viewPager)複製程式碼

這麼簡單又實現了一個功能:

下拉重新整理

相信很多人都接觸過 PullToRefresh 這樣的第三方下拉重新整理控制元件,現在我們來看看 Material Design 的下拉重新整理 SwipeRefreshLayout。

在佈局裡用 SwipeRefreshLayout 包裹要實現下拉重新整理的區域,到頂部下拉便會出現載入框,並觸發監聽器的 onRefresh 方法。我們這樣來監聽下拉重新整理:

swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
        load();
    }
};複製程式碼

可以通過 setRefreshing 方法主動開始或停止重新整理。

效果如下:

可摺疊標題欄

在 APP 裡,經常會有一個標題欄,用於顯示頁面標題、放置搜尋或設定按鈕。它的作用不言而喻,但在寸土寸金的螢幕裡它卻一直佔據著一塊固定的區域。現在,我們能夠實現在合適的時候將標題欄摺疊起來。下面簡單介紹一下涉及到的幾個控制元件。

Toolbar

經過 TitleBar、ActionBar 的發展,現在我們用 Toolbar 來實現標題欄:

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"/>複製程式碼

在程式碼中初始化:

setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
    // 設定左邊按鈕可點選
    actionBar.setDisplayHomeAsUpEnabled(true);
}複製程式碼

AppBarLayout

AppBarLayout 繼承自 LinerLayout,支援滑動。要讓 Toolbar 響應摺疊效果,需要把 AppBarLayout 作為 Toolbar 的父佈局。

被 AppBarLayout 包裹的控制元件,可以設定一個 layout_scrollFlags 屬性,即滑動摺疊的型別。我給 Toolbar 設定了

app:layout_scrollFlags="scroll|enterAlways"複製程式碼

代表頁面向上、向下滾動時,Toolbar 會跟著一起向上、向下滾動,直到完全隱藏或完全顯示。

CoordinatorLayout

單有 AppBarLayout 還不行,還要配合 CoordinatorLayout 使用才能實現摺疊效果。CoordinatorLayout 作為一個上層佈局,用來協調它的子佈局間的互動。

綜合上面幾個控制元件,最終佈局如下:

<?xml version="1.0" encoding="utf-8"?>
<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.support.design.widget.AppBarLayout
        android:id="@+id/appbar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="scrollable"
            app:theme="@style/ThemeOverlay.AppCompat.Dark" />

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

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </android.support.v4.widget.SwipeRefreshLayout>

</android.support.design.widget.CoordinatorLayout>複製程式碼

要和 Toolbar 實現聯動的控制元件,需要實現了 NestedScrollingChild 介面,並且配置「app:layout_behavior="@string/appbar_scrolling_view_behavior"」。這裡常用的控制元件有 SwipeRefreshLayout、NestedScrollView、RecyclerView。

基本都是佈局程式碼,就可以實現這樣的效果:

CollapsingToolbarLayout

除了簡單的將 Toolbar 收起,還可以實現將圖片收縮轉換為 Toolbar 的酷炫效果。要實現這個效果需要使用 CollapsingToolbarLayout,用它包裹 ImageView 和 Toolbar:

<?xml version="1.0" encoding="utf-8"?>
<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.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="200dp">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/image_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />

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

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

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <WebView
            android:id="@+id/web_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>複製程式碼

CollapsingToolbarLayout 內的控制元件需要設定 layout_collapseMode 屬性,可選值有:

  • parallax 視差模式,隨著頁面滾動會有視差摺疊效果
  • pin 固定模式,完全摺疊後固定顯示

效果如下:

卡片檢視

在 Material Design 裡有一個概念是「卡片」,而 CardView 就是這個概念的直接體現。

CardView 其實就是一個帶有 MD 風格的 FrameLayout,它可以有水波紋點選效果,可以設定圓角、設定 z 軸高度(陰影)。

用的時候記得新增依賴:

compile "com.android.support:cardview-v7:${SUPPORT_LIB_VERSION}"複製程式碼

用法跟 FrameLayout 是一樣的,下面例子設定了水波紋點選效果以及圓角和 z 軸高度:

<android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:foreground="?attr/selectableItemBackground"
    app:cardCornerRadius="5dp"
    app:cardElevation="5dp">

    <!--...-->

</android.support.v7.widget.CardView>複製程式碼

共享元素動畫

在 Material Design 裡,希望頁面的跳轉能有一定的連貫性。

如果頁面 A 和頁面 B 擁有同一個元素,在 Android 5.0 之後,我們可以實現一種叫共享元素的過場動畫,這個元素將會從頁面 A 的位置,變換到頁面 B 的位置。

在「冰與火百科」中,首頁列表和詳情頁都顯示了一張人物圖片,就可以這樣做。

在詳情頁的 ImageView 配置一個 transitionName,它是元素轉換的一個標識:

<ImageView
    android:id="@+id/image_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:transitionName="tran_01"/>複製程式碼

在首頁跳轉的程式碼,修改為:

Pair[] pairs = new Pair[]{new Pair(binding.ivImg, "tran_01")};
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(mainActivity, pairs);
ActivityCompat.startActivity(context, intent, options.toBundle());複製程式碼

這就是共享元素的效果:

小結

藉著寫「冰與火百科」,把 Material Design 的部分常用控制元件歸納了一下。但這裡只說了每個控制元件的基本用法,想有更深入的瞭解,還需要看查閱相關文章。

我不太懂設計,但有了這些控制元件,感覺「冰與火百科」還挺好看的~

專案地址

相關文章