1. 程式人生 > >Android Material Design 相容庫的使用詳解

Android Material Design 相容庫的使用詳解

眾所周知Material Design(材質設計)是Google在2014年I/O大會上釋出的一種新的設計規範。一經推出就好評如潮,個人是非常喜歡這種風格的,由於他只支援5.0及其以上的裝置,開發者也只是去嚐嚐鮮,並沒用在真實的專案中去,使得其在國內的市場並不是太好。隨後不久Google就退出了其相容庫Android Design Support Library,相容至2.1!這絕對是業界良心,極大的便利了我們這些開發者。這一相容庫可以讓我們可以在自己的專案真實的去體驗一把。百度谷歌一下,其實現在已經有個很多介紹這個支援庫的文章,但是很多都並不太詳細,大部分讓人看了都似懂非懂,我之前也學習過這個庫的用法,很久沒看基本又忘了,最近我又重新開始學習了這個庫,所以本文也主要是對這次學習的一個總結,當然也參考了其他很多的文章,算是站在巨人的肩膀上吧,當然這篇文章也為後來的學習者有更多的參考資料。

先來幾張效果圖吧:

圖1 圖2

感興趣的也可以先看看,最後能做一個什麼樣的App:MDDemo.apk

說到這個相容庫,其實很多都是可以在Github去找到對應的效果的,其實我們也是可以去用Github上的那些開源專案,那麼多的庫都要一個一個去引用,還是非常的麻煩,我猜可能Google也是看到這些東西經常被開發者用在了專案中,索性自己將其封裝成了一個庫,以方便開發者的使用。這個庫我們應該怎樣去使用呢?這是本文講解的重點。首先我們看看這個庫怎麼去引用到我們的專案中:

compile ‘com.android.support:design:23.1.1’

我們只需將其新增到專案的依賴中去,然後同步一下就OK了。
添加了依賴之後我們再來看看這個庫裡面都有哪些控制元件吧,如下圖,紅色邊框矩形框出來的就是這個庫裡面包含的控制元件,我們可以看到有AppBarLayout、CollapsingToolbarLayout、CoordinatorLayout,FloatingActionButton、NavigationView、Snackbar、TabLayout、TextInputLayout

這八種,後面我會一個一個介紹用法,如果還沒學過這些控制元件使用的本文可以手把手教你學會怎麼去使用,如果之前有了解過的或者對這個還比較熟悉的,也可以繼續看下去,就當做溫習一下,順便還可以幫我找出有表達不正確的地方。

庫中包含的控制元件.png

1.Snackbar

SnackBar通過在螢幕底部展示簡潔的資訊,為一個操作提供了一個輕量級的反饋,並且在Snackbar中還可以包含一個操作,在同一時間內,僅且只能顯示一個 Snackbar,它的顯示依賴於UI,不像Toast那樣可以脫離應用顯示。它的用法和Toast很相似,唯一不同的就是它的第一個引數不是傳入Context而是傳入它所依附的父檢視,但是他比Toast更強大。
我們來看看它的基本使用
<pre>
Snackbar.make(mDrawerLayout, “SnackbarClicked”, Snackbar.LENGTH_SHORT).show();</pre>

是不是和Toast很相似。我們來看看它的效果圖:

SnackWithoutAction

是不是看著比Toast舒服多了

好了,我們再來看看一個包含Action的Snackbar怎麼使用
<pre>
Snackbar.make(mDrawerLayout, “SnackbarClicked”, Snackbar.LENGTH_SHORT).setAction(“Action”, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, “I’m a Toast”, Toast.LENGTH_SHORT).show();
}
}).setActionTextColor(Color.RED).show();

</pre>

這裡我給Snackbar設定了一個Action,設定其文字顏色為紅色,並帶有了一個點選事件,在單擊這個Action後就彈出一個Toast,效果圖如下


SnackbarWithAction

2.TextInputLayout

使用過EditText的同學肯定知道,有一個叫hint的屬性,它可以提示使用者此處應該輸入什麼內容,然而當用戶輸入真實內容之後,hint的提示內容就消失了,使用者的體驗效果是十分不好的,TextInputLayout的出現解決了這個問題。

我們來看看這個控制元件的是怎麼使用的
<pre>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android
xmlns:tools=”http://schemas.android.com/tools
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
android:padding=”10dp”
tools:context=”burgess.com.materialdesignstudy.MDTextViewActivity”>

<!--TextInputLayout的顏色來自style中的colorAccent的顏色-->
<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <EditText
        android:id="@+id/edit_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="使用者名稱/手機號"
        android:inputType="textEmailAddress" />


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

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

    <EditText
        android:id="@+id/edit_pwd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="密碼"
        android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>

</LinearLayout>

</pre>

其實這個控制元件的使用非常簡單,我們只需在EditText外面再巢狀一個TextInputLayout就行了。我們來看看效果

TextInputLayout

還是很不錯的,當用戶在輸入的時候hint的內容就會跑到輸入內容的上邊去,其中TextInputLayout中字型的顏色是style檔案中的colorAccent(關於colorAccent是什麼,文末有一張圖看了就清楚了)的顏色。

3.FloatingActionButton

FloatingActionButton從名字可以看出它是一個浮動的按鈕,它是一個帶有陰影的圓形按鈕,可以通過fabSize來改變其大小,主要負責介面的基本操作,這個按鈕總體來說還是比較簡單的。
我們來看看它的一些屬性:

  • 預設FloatingActionButton 的背景色是應用主題的 colorAccent(其實MD中的控制元件主題預設基本都是應用的這個主題),可以通過app:backgroundTint 屬性或者setBackgroundTintList (ColorStateList tint)方法去改變背景顏色。
  • 上面提到 Floating action button 的大小尺寸,可以用過**app:fabSize **屬性設定(normal or mini)
  • ** android:src** 屬性改變 drawable
  • app:rippleColor設定點選 button 時候的顏色(水波紋效果)
  • app:borderWidth設定 button 的邊框寬度
  • app:elevation設定普通狀態陰影的深度(預設是 6dp)
  • app:pressedTranslationZ設定點選狀態的陰影深度(預設是 12dp)

怎麼去使用:
<pre><android.support.design.widget.FloatingActionButton
android:id=”@+id/fab_search”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”bottom|end”
android:layout_margin=”16dp”
android:src=”@android:drawable/ic_dialog_email”
app:borderWidth=”2dp”
app:fabSize=”normal”
app:rippleColor=”#ff0000” />
</pre>
其效果圖如下:


FloatingActionButton

4.CoordinatorLayout

我們來看看官方對他的描述:

  • CoordinatorLayout is a super-poweredFrameLayout.
  • CoordinatorLayout is intended for two primary use cases:
    1.As a top-level application decor or chrome layout
    2.As a container for a specific interaction with one or more child views

從這裡我們可以知道它是一個增強版的FrameLayout,他可以協調其子View的互動,控制手勢機滾動技巧。這個控制元件十分的強大,用處也十分的廣泛。就比如剛才說的FloatingActionButton如果用CoordinatorLayout 作為FloatingActionButton的父佈局,它將很好的協調Snackbar的顯示與FloatingActionButton。


沒有使用CoordinatorLayout 作為父佈局的FloatingActionButton顯示Snackbar
使用了CoordinatorLayout 作為父佈局的FloatingActionButton顯示Snackbar

這兩個動態圖來源於網上,效果是顯而易見的,原諒我用的手機測試,所以沒法錄製動態圖(如果有人知道怎麼去錄製的請告知)。
還有它的另外一個比較重要的用處是控制協調內容區和AppBarLayout的滾動,這個後面會講到。這個控制元件還是比較複雜的,用多了就能慢慢體會到他的強大之處。我對這個控制元件的還不是那麼的清楚,所以在這裡提供一篇關於CoordinatorLayout 用法的譯文: 掌握 Coordinator Layout

5.NavigationView

抽屜式的導航控制元件,有了他我們可以使得導航更簡單。我們可以直接通過選單資原始檔生成所需的元素。下圖左邊的即是NavigationView。

NavigationView

下面我們來看看它是怎麼實現的吧。
<pre>
<?xml version=”1.0” encoding=”utf-8”?>

<!--內容區-->
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

    <FrameLayout
        android:id="@+id/frame_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/appbar"
        android:scrollbars="none"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

<!--左側導航選單-->
<android.support.design.widget.NavigationView
    android:id="@+id/navigation_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/navigation_header"
    app:menu="@menu/drawer" />

</android.support.v4.widget.DrawerLayout>
</pre>

可以看到這裡我們是以DrawerLayout作為其父佈局,對於DrawLayout他可以實現一種抽屜式的側滑效果,這裡不多做講解,這裡只簡單說一點:DrawLayout中的第一個佈局是內容佈局,第二個是選單佈局。現在我們直接定位到NavigationView,我們看到這裡有** app:headerLayout=”@layout/navigation_header”app:menu=”@menu/drawer”**這兩行程式碼,其中headerLayout是設定其頭部的佈局,這個佈局我們可以隨便寫,就和寫普通的佈局檔案一樣的。對於menu就是選單項的配置了,其配置檔案如下:
<pre>
<?xml version=”1.0” encoding=”utf-8”?>
<menu xmlns:android=”http://schemas.android.com/apk/res/android
xmlns:tools=”http://schemas.android.com/tools
tools:context=”.MainActivity”>
<group android:checkableBehavior=”single”>
<item
android:id=”@+id/navigation_item_home”
android:icon=”@drawable/iconfont_home”
android:title=”首頁” />
<item
android:id=”@+id/navigation_item_blog”
android:icon=”@drawable/iconfont_blog”
android:title=”我的部落格” />

    <item
        android:id="@+id/navigation_item_about"
        android:icon="@drawable/iconfont_about"
        android:title="關於" />
</group>

</menu>
</pre>

就這麼簡單,我們要實現上述的效果就只要這些就足夠了。但是如果我們想要對Item新增一個點選的事件怎麼做呢?請看下面:
<pre>
private void setNavigationViewItemClickListener() {
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_item_home:
mToolbar.setTitle(“首頁”);
switchFragment(“MainFragment”);
break;
case R.id.navigation_item_blog:
mToolbar.setTitle(“我的部落格”);
switchFragment(“BlogFragment”);
break;
case R.id.navigation_item_about:
mToolbar.setTitle(“關於”);
switchFragment(“AboutFragment”);
break;
default:
break;
}
item.setChecked(true);
mDrawerLayout.closeDrawer(Gravity.LEFT);
return false;
}
});
}
</pre>
上面的程式碼很清楚了,就是為NavigationView添加了一個OnNavigationItemSelectedListener的監聽事件,然後我們就可以做我們想做的事了。

6. AppBarLayout、CollapsingToolbarLayout、TabLayout

這三個控制元件我用一個例子一下講解了,在實際的使用中這三個控制元件還是經常會組合在一起的。

  • AppBarLayout:其繼承於LinearLayout,使用AppBarLayout可以讓包含在其中的子控制元件能響應被標記了ScrollingViewBehavior的的滾動事件(要與CoordinatorLayout 一起使用),利用它我們可以很容易的去實現視差滾動效果,這樣所你可能還是不太懂,千言萬語不如一張圖來的直接爽快(這樣圖還是來源於網上,其藍色的部分就是AppBarLayout,內容區就是被標記了ScrollingViewBehavior的,可以看到效果是不是挺不錯的)。

  • CollapsingToolbarLayout :讓Toolbar可伸縮,在伸縮的時候決定ToolBar是移除螢幕和固定在最上面。由於Toolbar 只能固定到螢幕頂端並且不能做出好的動畫效果,所以才有了這個Layout的出現。

  • TabLayout:這個其實和我們之前使用的第三方庫ViewPagerIndicator是很類似的,一般都和ViewPager一起使用,效果如下圖:

    動態圖來源於網上

基本知道他們是幹什麼用的了,下面就讓我們動手,看看到底怎麼用吧。

先來幾張下面我們要實現的效果。由於我是真機測試,沒法錄製動態圖,這裡就用幾張靜態圖代替了。

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:expandedTitleMarginEnd="64dp"
        app:expandedTitleMarginStart="48dp"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

        <ImageView
            android:id="@+id/iv_book_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            android:scaleType="centerInside"
            android:transitionName="transition_book_img"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.7" />

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


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


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


<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

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

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

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#292929"
                android:textSize="18sp" />

            <TextView
                android:id="@+id/tv_rating"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="2dp"
                android:textColor="#FF6347"
                android:textSize="14sp" />
        </LinearLayout>


        <TextView
            android:id="@+id/tv_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="2dp"
            android:textColor="#575757"
            android:textSize="12sp" />


    </LinearLayout>

    <android.support.design.widget.TabLayout
        android:id="@+id/sliding_tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabGravity="fill"
        app:tabMode="scrollable" />

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

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

我們來分析一下這個佈局檔案:
1.最外層的佈局用的是CoordinatorLayout,因為這裡面有很多的動畫,CoordinatorLayout可以很好的去協調裡面的動畫。在android.support.design.widget.AppBarLayout下面的那個LinearLayout被標記了appbar_scrolling_view_behavior,這樣一來AppBarLayout就能響應LinearLayout中的滾動事件。
2.再來看看CollapsingToolbarLayout,其中contentScrim是設定其內容區的顏色,layout_scrollFlags取了scroll和exitUntilCollapsed兩個值。
layout_scrollFlags的Flag包括:

  • scroll: 所有想滾動出螢幕的view都需要設定這個flag,沒有設定這個flag的view將被固定在螢幕頂部。
  • **enterAlways**: 這個flag讓任意向下的滾動都會導致該view變為可見,當向上滑的時候Toolbar就隱藏,下滑的時候顯示。
    
  • **enterAlwaysCollapsed**: 顧名思義,這個flag定義的是何時進入(已經消失之後何時再次顯示)。假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那麼view將在到達這個最小高度的時候開始顯示,並且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。   
    
  • **exitUntilCollapsed**: 同樣顧名思義,這個flag時定義何時退出,當你定義了一個minHeight,這個view將在滾動到達這個最小高度的時候消失。
    

只看概覽可能還是不會太清楚,注意一定要多實踐,感興趣的讀者可以一個一個去試一試效果。

3.定位到ImageView,有這兩個屬性是我們平常用沒看到的,說明我寫在註釋上了
<pre>
app:layout_collapseMode=”parallax”//這個是配置當ImageView消失或者顯示時候有一種視差滾動效果
app:layout_collapseParallaxMultiplier=”0.7”//視差因子,越大視差特效越明顯,最大為1 </pre>
4.Toolbar中有一個app:layout_collapseMode=”pin” 這個確保Toolbar在AppBarLayout摺疊的時候仍然被固定在螢幕的頂部

佈局檔案就到這,我們來看看Activity中的邏輯

<pre>
/**

  • Created by _SOLID

  • Date:2016/3/30

  • Time:20:16
    */
    public class BookDetailActivity extends BaseActivity {

    private String mUrl;
    private Toolbar mToolbar;
    private CollapsingToolbarLayout mCollapsingToolbarLayout;
    private ImageView mIvBook;

    private BookBean mBookBean;
    private TextView mTvTitle;
    private TextView mTvMsg;
    private TextView mTvRating;
    private ViewPager mViewPager;
    private TabLayout mTabLayout;

    @Override
    protected void initView() {
    //設定Toolbar
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);
    getSupportActionBar().setHomeButtonEnabled(true);//決定左上角的圖示是否可以點選
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);//決定左上角圖示的右側是否有向左的小箭頭
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
    onBackPressed();
    }
    });

     mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout);
     mIvBook = (ImageView) findViewById(R.id.iv_book_image);
     mTvTitle = (TextView) findViewById(R.id.tv_title);
     mTvMsg = (TextView) findViewById(R.id.tv_msg);
     mTvRating = (TextView) findViewById(R.id.tv_rating);
     mViewPager = (ViewPager) findViewById(R.id.viewpager);
     mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
     mTabLayout.addTab(mTabLayout.newTab().setText("作者資訊"));
     mTabLayout.addTab(mTabLayout.newTab().setText("章節"));
     mTabLayout.addTab(mTabLayout.newTab().setText("書籍簡介"));
    

    }

    @Override
    protected int setLayoutResourseID() {
    return R.layout.activity_book_detail;
    }

    @Override
    protected void init() {
    mUrl = getIntent().getStringExtra(“url”);
    }

    @Override
    protected void initData() {
    SolidHttpUtils.getInstance().loadString(mUrl, new SolidHttpUtils.HttpCallBack() {
    @Override
    public void onLoading() {

         }
    
         @Override
         public void onSuccess(String result) {
             Gson gson = new Gson();
             mBookBean = gson.fromJson(result, BookBean.class);
             mCollapsingToolbarLayout.setTitle(mBookBean.getTitle());
             mTvTitle.setText(mBookBean.getTitle());
             mTvMsg.setText(mBookBean.getAuthor() + "/" + mBookBean.getPublisher() + "/" + mBookBean.getPubdate());
             mTvRating.setText(mBookBean.getRating().getAverage() + "分");
             SolidHttpUtils.getInstance().loadImage(mBookBean.getImages().getLarge(), mIvBook);
    
             BookInfoPageAdapter adapter = new BookInfoPageAdapter(BookDetailActivity.this, mBookBean, getSupportFragmentManager());
             mViewPager.setAdapter(adapter);
             mTabLayout.setupWithViewPager(mViewPager);
    
         }
    
         @Override
         public void onError(Exception e) {
    
         }
     });
    

    }
    }

</pre>

其實並不難,這裡只需注意一點,當Toolbar被CollapsingToolbarLayout包裹的時候,設定標題是設定CollapsingToolbarLayout的,而不是Toolbar。

好了終於寫完了,差不多寫了接近4個小時,好累的說,現在整個人都是暈暈的。

附(一些控制元件使用使用應注意的地方):

1.在使用CardView的時候,一定要注意,當CardView的寬和高填充父容器的時候,CardView的margin最好要比cardElevation大,不然就看不到立體的效果。

2.我們知道ListView有一個onItemClick的事件,但是RecyclerView卻沒有,那麼我們應該怎樣去設定呢?其實很簡單,關於RecyclerView設定item的點選事件,只需在建立ViewHolder的時候,給填充的View設定單擊事件即可。

3.在使用android.support.design.widget.AppBarLayout的時候內容區最好使用android.support.v4.widget.NestedScrollView,之前我的內容區用的是ScrollView,在往上拉的時候AppBarLayout一直沒有動畫效果,折騰了幾個小時都沒找到原因。最後逼不得用Android Studio建立了一個他自帶的關於AppBarLayout的模板專案,看到他用的是NestedScrollView作為內容區,我果斷就把我的內容區換成了這個,立馬就有動畫效果了。

NestedScrollView官方的描述:

NestedScrollView is just likeScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.

如果感覺還不錯的就給個喜歡支援一下吧,有問題請留言,謝謝

最後附一張MD的主題色解析圖:

MD主題色解析.jpg