1. 程式人生 > >Android學習筆記之Material Design實戰

Android學習筆記之Material Design實戰

Material Design是在2014年Google I/O大會上重磅推出的一套全新的介面設計語言,是由谷歌的設計工程師們基於傳統優秀的設計原則,結合豐富的創意和科學技術所發明的一套全新的介面設計語言,包含了視覺、運動、互動效果等特性。

為支援Material Design UI設計風格,谷歌在2015年的Google I/O大會上推出了一個Design Support庫,這個庫將Material Design中最具代表性的一些控制元件和效果進行了封裝,使得開發者在即使不瞭解Material Design的情況下也能非常輕鬆地將自己地應用Material化。

一、Toolbar

Toolbar將會是我們接觸地第一個Material控制元件,雖說對於Toolbar你暫時應該還是比較陌生地,但是對於它地另一個相關控制元件ActionBar,你應該有點熟悉。每個活動最頂部地那個標題欄其實就i是ActionBar,不過ActionBar由於其設計地原因,被限定只能位於活動地頂部,從而不能實現一些Material Design的效果,因此官方現在已經不再建議使用ActionBar了。

Toolbar的強大之處在於,它不僅繼承了ActionBar的所有功能,而且靈活性很高,可以配合其他控制元件來完成一些Material Design的效果。

首先你要知道,任何一個新建的專案,預設都是會顯示ActionBar的,那麼這個ActionBar到底是哪裡來的呢?其實這是根據專案中指定的主題來顯示的,AndroidManifest.xml中如下所示:

<application

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportRtl="true"

android:theme=‘@style/AppTheme"

>

...

</application>

可以看到,這裡使用android:theme屬性指定了一個AppTheme的主題,那麼這個AppTheme又是再哪裡定義的呢?開啟res/values/styles.xml檔案,程式碼如下:

<resources>

<!--Base application theme -->

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>

<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

<item name="colorAccent">@color/colorAccent</item>

</style>

</resources>

這裡定義了一個叫AppTheme的主題,然後指定它的parent主題是Theme.AppCompat.Light.DarkActionBar。這個DarkActionBar是一個深色的ActionBar主題,我們之前所有的專案中自帶的ActionBar就是因為指定了這個主題才出現的。

而現在我們準備使用Toolbar來替代ActionBar,因此需要指定一個不帶ActionBar的主題,通常Theme.AppCompat.NoActionBar和ThemeAppCompat.Light.NoActionBar這兩種主題可選,其中Theme.AppCompat.NoActionBar表示深色主題,它會將介面的主體顏色設成深色,陪襯顏色設成淡色。而Theme.AppCompat.Light.NoActionBar表示淡色主題,他會將介面的主體顏色設成淡色,陪襯顏色設成深色。具體效果你可以試一試,這裡由於我們之前的程式一直都是以淡色為主的,那麼我就選用淡色主題了了,如下所示:

<resources>

<!--Base application theme -->

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>

<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

<item name="colorAccent">@color/colorAccent</item>

</style>

</resources>

然後觀察一下AppTheme中的屬性重寫,這裡重寫了colorPrimary、colorPrimaryDark和colorAccent這3個屬性的顏色。除上述3個屬性之外,我們還可以通過textColorPrimary、windowBackground和navigationBarColor等屬性來控制更多位置的顏色。不過唯獨colorAccent這個屬性比較難理解,它不只是用來指定這樣一個按鈕的顏色,而是更多表達了一個強調的意思,比如一些控制元件的選中狀態也會使用這個顏色。

現在我們已經將ActionBar隱藏起來了,那麼接下來看一看如何使用Toolbar來替代ActionBar。修改activity_material_design_test.xml中的程式碼,如下所示:

<FrameLayout 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.v7.widget.Toolbar

android:id="@+id/toolbar"

android:layout_width="match_parent"

android:layout_height="?attr/actionBarSize"

android:background="?attr/colorPrimary"

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

</FrameLayout>

toolbar.xml程式碼如下:

<?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"
>
    <item
android:id="@+id/backup"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:title="Backup"
app:showAsAction="always"
/>
    <item
android:id="@+id/delete"
android:icon="@android:drawable/ic_menu_delete"
android:title="Delete"
app:showAsAction="ifRoom"
/>
    <item
android:id="@+id/settings"
android:icon="@android:drawable/ic_menu_more"
android:title="Settings"
app:showAsAction="never"
/>
</menu>

使用app:showAsAction來指定按鈕的顯示位置,之所以這裡再次使用了app名稱空間,同樣是為了能夠相容低版本的系統。showAsAction主要有以下幾種值可選:always表示永遠顯示在Toolbar中,如果螢幕控制元件不夠則不顯示;ifRoom表示螢幕控制元件足夠的情況下顯示在Toolbar中,不夠的化就顯示在選單當中;never則表示永遠顯示在選單當中。注意,Toolbar中的action按鈕只會顯示圖示,選單中的action按鈕只會顯示文字。

主程式碼如下:

public class MaterialDesignTestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
setContentView(R.layout.activity_material_design_test);
initUI();
}

    public void initUI(){

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
        if(actionBar!=null){
            actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.mipmap.ic_launcher);
}}

    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.toolbar,menu);
        return true;
}

    public boolean onOptionsItemSelected(MenuItem item){
        switch(item.getItemId()){            case R.id.backup:
                Toast.makeText(this,"You clicked Backup",Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete:
                Toast.makeText(this,"You clicked Delete",Toast.LENGTH_SHORT).show();
                break;
            case R.id.settings:
                Toast.makeText(this,"You clicked Settings",Toast.LENGTH_SHORT).show();
                break;
            default:
        }
        return true;
}
}

二、滑動選單

滑動選單可以說是Material Design中最常見的效果之一了。所謂的滑動選單就是將一些選單選項隱藏起來,而不是放置在主螢幕上,然後通過滑動的方式將選單顯示出來。這種方式既節省了螢幕空間,又實現了非常好的動畫效果,是Material Design中推薦的做法。

1.DrawerLayout

activity_material_design_test.xml中程式碼如下:

<android.support.v4.widget.DrawerLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

android:id="@+id/drawer_layout"

android:layout_width="match_parent"

android:layout_height="match_parent"

>

<FrameLayout

android:layout_width="match_parent"

android:layout_height="match_parent"

>

<android.support.v7.widget.Toolbar

android:id="@+id/toolbar"

android:layout_width="match_parent"

android:layout_height="?attr/actionBarSize"

android:background="?attr/colorPrimary"

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

app:popupTheme="@style/ThemeOverlay.AppCompat.Light"

/>

</FrameLayout>

<TextView

android:layout_width=‘match_parent"

android:layout_height="match_parent"

android:layout_gravity="start"

android:text="This is menu"

android:textSize="30sp"

android:background="#FFF"

/>

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

可以看到,這裡最外層的控制元件使用了DrawerLayout,這個控制元件是由support-v4庫提供的。DrawerLayout中放置了兩個直接子控制元件,第一個子空間是FrameLayout,用於作為主螢幕顯示的內容;第二個子控制元件這裡使用了一個TextView,用於作為滑動選單中顯示的內容,起始使用什麼都可以,DrawerLayout並沒有限制只能使用固定的控制元件。

但是關於第二個子控制元件有一點需要注意,layout_gravity這個屬性是必須指定的,因為我們需要告訴DrawerLayout滑動選單是在螢幕的左邊還是右邊,指定left表示滑動選單在左邊,指定right滑動選單在右邊,這裡指定start,表示會根據系統語言進行判斷。

public class MaterialDesignTestActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
setContentView(R.layout.activity_material_design_test);
initUI();
}

    public void initUI(){

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawaer_layout);
ActionBar actionBar = getSupportActionBar();
        if(actionBar!=null){
            actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.mipmap.ic_launcher);
}

        /*findViewById(R.id.tv_menu).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDrawerLayout.closeDrawers();
            }
        });*/
}

    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.toolbar,menu);
        return true;
}

    public boolean onOptionsItemSelected(MenuItem item){
        switch(item.getItemId()){
            case android.R.id.home:
                mDrawerLayout.openDrawer(GravityCompat.START);
                break;
            case R.id.backup:
                Toast.makeText(this,"You clicked Backup",Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete:
                Toast.makeText(this,"You clicked Delete",Toast.LENGTH_SHORT).show();
                break;
            case R.id.settings:
                Toast.makeText(this,"You clicked Settings",Toast.LENGTH_SHORT).show();
                break;
            default:
        }
        return true;
}
}

2.NavigationView

NavigationView是Design Support庫中提供的一個控制元件,它不僅是嚴格按照MaterialDesign的要求來進行設計的,而且還可以將滑動選單頁面的實現變得非常簡單。

首先,這個控制元件是Design Support庫中提供的,俺麼我們就需要將這個庫引入到專案中才行。開啟app/build.gradle檔案,再dependencies閉包中新增如下內容:

dependencies{

compile fileTree(dir:'libs',include:['*.jar'])

compile 'com.android.support.appcompat-v7:24.2.1'

testCompile 'junit:junit:4.12'

compile 'com.android.support;design;24.2.1'

compile 'de.hdodenhof:circleimageview:2.1.0'

}

這裡添加了兩行依賴關係,第一行就是Design Support庫,第二行是一個開源專案CircleImageView,它可以用來輕鬆實現圖片圖形化的功能,我們待會就會用到它。CircleImageView的專案主頁地址是:https://github.com/hdodenhof/CircleImageView。

在開始使用之前,我們還需要提前準備好兩個東西:menu和headerLayout。menu是用來在NavigationView中顯示具體的選單項的,headerLayout則是用來在NavigationView中顯示頭部佈局的。

nav_menu.xml程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
android:id="@+id/nav_call"
android:icon="@android:drawable/ic_menu_call"
android:title="Call"
/>
        <item
android:id="@+id/nav_friends"
android:icon="@android:drawable/ic_menu_agenda"
android:title="Friends"
/>
        <item
android:id="@+id/nav_location"
android:icon="@android:drawable/ic_menu_mylocation"
android:title="Location"
/>
        <item
android:id="@+id/nav_mail"
android:icon="@android:drawable/ic_menu_report_image"
android:title="Mail"
/>
        <item
android:id="@+id/nav_task"
android:icon="@android:drawable/ic_menu_recent_history"
android:title="Tasks"
/>
    </group>
</menu>

nav_header.xml程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp"
android:padding="10dp"
android:background="?attr/colorPrimary"
>
    <de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/icon_iamge"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@mipmap/ic_launcher"
android:layout_centerInParent="true"
/>
    <TextView
android:id="@+id/mail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="[email protected]"
android:textColor="#FFF"
android:textSize="14sp"
/>
    <TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/mail"
android:text="Tony Green"
android:textColor="#FFF"
android:textSize="14sp"
/>
</RelativeLayout>

修改activity_main.xml程式碼,如下所示:

<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/drawaer_layout"
>
    <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
        <android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
    </FrameLayout>
<!--<TextView
        android:id="@+id/tv_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:text="This is menu"
        android:textSize="30sp"
        android:background="#FFF"
        />-->
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/nav_menu"
app:headerLayout="@layout/nav_header"
/>
</android.support.v4.widget.DrawerLayout>

這裡通過app:menu和app:headerLayout屬性將我們剛才準備好的menu和headerLayout設定了進去,這樣NavigationView就定義完成了。

public class MaterialDesignTestActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
setContentView(R.layout.activity_material_design_test);
initUI();
}

    public void initUI(){

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawaer_layout);
NavigationView navView = (NavigationView) findViewById(R.id.nav_view);
ActionBar actionBar = getSupportActionBar();
        if(actionBar!=null){
            actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.mipmap.ic_launcher);
}

        navView.setCheckedItem(R.id.nav_call);
navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener(){
            @Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                mDrawerLayout.closeDrawers();
                return true;
}
        });
/*findViewById(R.id.tv_menu).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDrawerLayout.closeDrawers();
            }
        });*/
}

    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.toolbar,menu);
        return true;
}

    public boolean onOptionsItemSelected(MenuItem item){
        switch(item.getItemId()){
            case android.R.id.home:
                mDrawerLayout.openDrawer(GravityCompat.START);
                break;
            case R.id.backup:
                Toast.makeText(this,"You clicked Backup",Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete:
                Toast.makeText(this,"You clicked Delete",Toast.LENGTH_SHORT).show();
                break;
            case R.id.settings:
                Toast.makeText(this,"You clicked Settings",Toast.LENGTH_SHORT).show();
                break;
            default:
        }
        return true;
}
}

三、懸浮按鈕和可互動提示

1.FloatingActionButton

FloatingActionButton是Design Support庫中提供的一個控制元件,這個控制元件可以幫助我們比較輕鬆地實現懸浮按鈕地效果。

佈局程式碼如下所示:

<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/drawaer_layout"
>
    <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
        <android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
        <android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@android:drawable/ic_menu_share"
app:elevation="8dp"
/>
    </FrameLayout>
</android.support.v4.widget.DrawerLayout>

這裡使用app:elevation屬性來給FloatingActionButton指定一個高度值,高度值越大,投影範圍也越大,但是投影效果越淡,高度值越小,投影範圍也越小,但是投影效果越濃。

程式碼中FloatingActionButton點選事件處理如下:

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener(){
    @Override
public void onClick(View view) {
        Toast.makeText(MaterialDesignTestActivity.this,"FAB clicked",Toast.LENGTH_SHORT).show();
}
});

2.Snackbar

提示工具Snackbar並不是Toast的替代品,它們兩者之間有著不同的應用場景。Toast的作用是告訴使用者現在發生了什麼事情,但同時使用者只能被動接收這個事情,因為沒有什麼辦法讓使用者進行選擇。而Snackbar則在這方面進行了擴充套件,它允許在提示當中加入一個可互動按鈕,當用戶點選按鈕的時候可以執行一些額外的邏輯操作。打個比方,如果我們在執行刪除操作的時候只彈出一個Toast提示,那麼使用者要是誤刪了後果很嚴重,但是如果我們增加一個Undo按鈕,就相當於給使用者提供了一種彌補措施,從而大大降低了事故發生的概率,提升了使用者體驗。

Snackbar的用法也非常簡單,它和Toast是基本相似的,只不過可以額外增加一個按鈕的點選事件。

程式碼中實現如下所示:

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener(){
    @Override
public void onClick(View view) {

        Snackbar.make(view,"Data delete", Snackbar.LENGTH_SHORT).
                setAction("Undo",new View.OnClickListener(){
                    @Override
public void onClick(View view) {
                        Toast.makeText(MaterialDesignTestActivity.this,"Data restored",Toast.LENGTH_SHORT).show();
}
                }).show();
}
});

Snackbar的make()方法來建立一個Snackbar物件,make()方法的第一個引數需要傳入一個View,只要是當前姐買你佈局的任意一個View都可以,Snackbar會使用這個View來自動查詢最外層的佈局,用於展示Snackbar。第二個引數就是Snackbar中顯示的內容,第三個引數是Snackbar顯示的時長。接著又呼叫了一個setAction()方法來設定一個動作,從而讓Snackbar不僅僅是一個提示,而是可以和使用者進行互動的。最後呼叫show()方法讓Snackbar顯示出來。

不管是出現還是消失,Snackbar都是帶有動畫小夥的,因此視覺體驗也會比較好。

不過你有沒有發現一個bug,這個Snackbar竟然將我們的懸浮按鈕給遮擋住了。雖然說也不是什麼重大問題,因為Snackbar過一會兒就會自動消失,但這種使用者體驗總歸是不友好的。有沒有什麼辦法能解決一下呢?當然有,只需要藉助CoordinatorLayout就可以輕鬆解決。

3.CoordinatorLayout

CoordinatorLayout可以說是一個加強版的FrameLayout,這個庫也是由Design Support庫提供的。它在普通情況下的作用和FrameLayout基本一致。事實上,CoordinatorLayout可以監聽其所有子控制元件的各種事件,然後自動幫助我們做出最為合理的響應。

舉個簡單的例子,剛才彈出的Snackbar提示將懸浮按鈕遮擋住了,而如果我們能讓CoordinatorLayout監聽到Snackbar的彈出事件,那麼它會自動將內部的FloatingActionButton向上偏移,從而確保不會被Snackbar遮擋到。

將原佈局中的FrameLayout替換成CoordinatorLayout,程式碼如下:

<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/drawaer_layout"
>
    <android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
        <android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
        <android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@android:drawable/ic_menu_share"
app:elevation="8dp"
/>
    </android.support.design.widget.CoordinatorLayout>
<!--<TextView
        android:id="@+id/tv_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:text="This is menu"
        android:textSize="30sp"
        android:background="#FFF"
        />-->
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/nav_menu"
app:headerLayout="@layout/nav_header"
/>
</android.support.v4.widget.DrawerLayout>

由於CoordinatorLayout本身就是一個加強版的FrameLayout,因此這種替換不會有任何的副作用。

可以看到,懸浮按鈕自動向上偏移了Snackbar的同等高度,從而確保不會被遮擋住,當Snackbar消失的時候,懸浮按鈕會自動向下偏移回到原來位置。

另外懸浮按鈕的向上和向下偏移也是伴隨著動畫效果的,且和Snackbar完全同步,整體效果看上去特別賞心悅目。

不過我們回過頭來再思考一下,剛才說的是CoordinatorLayout可以監聽其所有子控制元件的各種事件,但是Snackbar好像並不是CoordiantorLayout的子控制元件吧,為什麼它卻可以被監聽到呢?

其實道理很簡單,還記得我們再Snackbar的make()方法中傳入的第一個引數嗎?這個引數就是用來指定Snackbar是基於哪個View來出發的,剛才我們傳入的是FloatingActionButton本身,而其是CoordinatorLayout中的子控制元件,因此這個事件就理所應當能被監聽到了。你可以做個試驗,如果給Snackbar的make()方法出入一個DrawerLayout,那麼Snackbar就會再次遮擋住懸浮按鈕,因為DrawerLayout不是CoordinatorLayout的子控制元件,CoordinatorLayout也就無法監聽到Snackbar的彈出和隱藏事件了。

四、卡片式佈局

卡片式佈局也是Materials Design中提出的一個新概念,它可以讓頁面中的元素看起來就像是再卡片中一樣,並且還鞥你擁有圓角和投影。

1.CardView

CardView是用於實現卡片式佈局效果的重要控制元件,由appcompat-v7庫提供。實際上,CardView也是一個FrameLayout,只是額外提供了圓角和陰影等效果,看上去會有立體的感覺。

eg.

<android.support.v7.widget.CardView

android:layout_width=’match_parent"

android:layout_height="wrap_content"

app:cardCornerRadius=”4dp“

app:elevation="5dp"

>

<TextView

android:id="@+id.info_text"

android:layout_width="match_parent"

android:layout_height="wrap_content"

/>

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

這裡定義了一個CardView佈局,我們可以通過app;cardCornerRadius屬性指定卡片圓角的弧度,數值遠大,圓角的弧度也越大。另外還可以通過app:elevation屬性指定卡片的高度,高度值越大,投影範圍也越大,但是投影效果越淡,高度值越小,投影範圍也越小,但是投影效果越濃。

新增波紋點選效果

預設情況,CardView是不可點選的,並且沒有任何的觸控反饋效果。觸控反饋動畫在使用者點選CardView時可以給使用者以視覺上的反饋。為了實現這種行為,你必須提供一下屬性:

<android.support.v7.widget.CardView
  ...
  android:clickable="true"
  android:foreground="?android:attr/selectableItemBackground">
  ...
</android.support.v7.widget.CardView>

使用android:foreground=”?android:attr/selectableItemBackground”可以使CardView點選產生波紋的效果,有觸控點向外擴散。

常見屬性

cardBackgroundColor -----》 設定cardview背景顏色

cardCornerRadius -----》設定cardview 圓角的半徑

cardElevation----》設定cardview的陰影

contentPadding-----》設定cardview的內間距

5.x上面可以設定點選水波紋效果

android:foreground="?attr/selectableItemBackground"

cardPreventCornerOverlap ------》防止api20之前圓角與內容重疊 ,在21以後不會重疊

cardUseCompatPadding ----》設定為true 可以為不同L版提供內間距相容 ,預設為false

eg.實現圖片列表

依賴庫:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
})

    compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
compile 'com.android.support:design:25.3.1'compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.android.support:cardview-v7:25.3.1'
}
這裡添加了一個Glide庫的依賴。Glide是一個超級強大的圖片載入庫,它不僅可以用於載入本地圖片,還可以載入網路圖片、GIF圖片、甚至是本地視訊。最重要的是,Glide的用法非常簡單,只需一行程式碼就能輕鬆實現複雜的圖片載入功能。Glide的專案主頁地址是:https://github.com/bumptech.glide。主佈局程式碼:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/drawaer_layout"
>
    <android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
        <android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
        <android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
        <android.support.design.widget.FloatingActionButton
android:id