第一行代碼(十二)

第十二章主要講了 Material Design 的一些用法

一、Maternal Design 介紹

??Android 平臺(tái)的界面風(fēng)格長(zhǎng)期難以統(tǒng)一,為了解決這個(gè)問題,Google 推出了全新的界面設(shè)計(jì)語言--Material Design.
??從 Android5.0開始,就將所有內(nèi)置的應(yīng)用都使用 Material Design 風(fēng)格來進(jìn)行設(shè)計(jì)。谷歌在2015年 Google I/O 大會(huì)上推出了 Design Support 庫。

二、Toolbar

??每個(gè)活動(dòng)最頂部的那個(gè)標(biāo)題欄其實(shí)就是 ActionBar,不過 ActionBar 由于其設(shè)計(jì)原因,被限定只能位于活動(dòng)頂部,從而不能實(shí)現(xiàn)一些 Material Design 的效果,因此官方已經(jīng)不再建議使用 ActionBar 了。
??ToolBar 的強(qiáng)大之處在于,它不僅繼承了 ActionBar 的所有功能,而且靈活性很高,可以配合其他控件一起來完成一些 Material Design 的效果。

注意:任何一個(gè)新建的項(xiàng)目,默認(rèn)都會(huì)顯示 ActionBar 的,這個(gè) ActionBar 是根據(jù)項(xiàng)目中指定的主題來顯示的。打開清單文件,找到 <application>標(biāo)簽中有一個(gè) android:theme 屬性,并且指定了一個(gè) AppTheme 主題。

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

??這個(gè) DarkActionBar 就是一個(gè)深色的 ActionBar 主題,項(xiàng)目中自帶的 ActionBar 就是因?yàn)橹付诉@個(gè)主題才出現(xiàn)的。但是我們現(xiàn)在要使用 ToolBar,所以要指定一個(gè)不帶 ActionBar 的主題,通常有 Theme.AppCompat.NoActionBar 和 Theme.AppCompat.Light.NoActionBar 這兩種主題,其中 Theme.AppCompat.NoActionBar 表示深色主題,他會(huì)將界面的主體顏色設(shè)置成深色,陪襯顏色設(shè)成淡色。而 Theme.AppCompat.Light.NoActionBar 表示淡色主題,它會(huì)將界面的主體顏色設(shè)置成淡色,陪襯顏色設(shè)成深色。
??觀察一下 AppTheme 中的屬性重寫,這里重寫了 colorPrimary、colorPrimaryDark 和 colorAccent 這3個(gè)屬性的顏色。

image.png

其他:

  • 1.colorPrimary:應(yīng)用的主要色調(diào),actionBar默認(rèn)使用該顏色,Toolbar導(dǎo)航欄的底色
  • 2.colorPrimaryDark:應(yīng)用的主要暗色調(diào),statusBarColor默認(rèn)使用該顏色
  • 3.statusBarColor:狀態(tài)欄顏色,默認(rèn)使用colorPrimaryDark
  • 4.windowBackground:窗口背景顏色
  • 5.navigationBarColor:底部欄顏色
  • 6.colorForeground:應(yīng)用的前景色,ListView的分割線,switch滑動(dòng)區(qū)默認(rèn)使用該顏色
  • 7.colorBackground:應(yīng)用的背景色,popMenu的背景默認(rèn)使用該顏色
  • 8.colorAccent:CheckBox,RadioButton,SwitchCompat等一般控件的選中效果默認(rèn)采用該顏色
  • 9.colorControlNormal:CheckBox,RadioButton,SwitchCompat等默認(rèn)狀態(tài)的顏色。
  • 10.colorControlHighlight:控件按壓時(shí)的色調(diào)
  • 11.colorControlActivated:控件選中時(shí)的顏色,默認(rèn)使用colorAccent
  • 12.colorButtonNormal:默認(rèn)按鈕的背景顏色
  • 13.editTextColor:默認(rèn)EditView輸入框字體的顏色。
  • 14.textColor:Button,textView的文字顏色
  • 15.textColorPrimaryDisableOnly:RadioButton checkbox等控件的文字
  • 16.textColorPrimary:應(yīng)用的主要文字顏色,actionBar的標(biāo)題文字默認(rèn)使用該顏色
  • 17.colorSwitchThumbNormal:switch thumbs 默認(rèn)狀態(tài)的顏色

??使用了 NoActionBar 主題后,我們已經(jīng)將 ActionBar 隱藏起來了,接下來就要使用 ToolBar 代替 ActionBar 了。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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.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.Toolbar>

</FrameLayout>

這里首先記得添加 xmlns:app 指定命名空間,因?yàn)?Material Design 是在 Android5.0系統(tǒng)中才出現(xiàn)的,而很多 Material 屬性在5.0之前的系統(tǒng)中并不存在,為了兼容之前的老系統(tǒng),我們不能使用 android:attribute 這樣的寫法了,就要使用 app:attribute 的寫法。

我們使用了 ToolBar 控件,該控件是 appcompat-v7庫提供的,高度指定為 ActionBar 的高度,背景色設(shè)置為 colorPrimary。接下來,我們?cè)O(shè)置了主題,因?yàn)槲覀冊(cè)?style.xm里面將程序的主題設(shè)置成了淡色主題,因此 ToolBar 也是淡色主題,ToolBar 上的元素就會(huì)自動(dòng)使用深色主題,看起來很難看,為了讓 ToolBar 單獨(dú)使用深色主題,我們就使用了 theme 為 ToolBar 單獨(dú)指定了一個(gè)主題:ThemeOverlay.AppCompat.Dark.ActionBar,但是這樣又會(huì)有一個(gè)問題,如果 ToolBar 中有菜單按鈕,彈出的菜單項(xiàng)會(huì)變成深色主題,又變得很難看,于是使用了 app:popupTheme屬性單獨(dú)將彈出的菜單指定成了淡色主題。

        Toolbar toolBar = (Toolbar) findViewById(R.id.toolbar);
        /*
            這句話的作用是:既使用了 ToolBar,又讓它的外觀和功能都和 ActionBar 一致
         */
        setSupportActionBar(toolBar);
        <activity android:name=".MaterialDesignActivity"
            android:label="Material Design">
            <!--這里我們?yōu)?Activity 指定了一個(gè) label,作用就是讓在 ToolBar 中顯示標(biāo)題-->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

??接下來我們給 ToolBar 添加一個(gè) action 按鈕


image.png

image.png
<?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="@mipmap/ic_launcher"
        android:title="Backup"
        app:showAsAction="always" />

    <item
        android:id="@+id/delete"
        android:icon="@mipmap/ic_launcher"
        android:title="delete"
        app:showAsAction="ifRoom" />

    <item
        android:id="@+id/settings"
        android:icon="@mipmap/ic_launcher"
        android:title="settings"
        app:showAsAction="never" />
</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">

    <!--使用 showAsAction 來指定按鈕的顯示位置 ,
        這里之所以使用 app 命名空間,是為了能夠
        兼容低版本-->

    <!-- always 表示永遠(yuǎn)顯示在 ToolBar 上,如果屏幕空間不夠則不顯示 -->
    <item
        android:id="@+id/backup"
        android:icon="@mipmap/ic_launcher"
        android:title="Backup"
        app:showAsAction="always" />

    <!--ifRoom 表示如果屏幕空間足夠就顯示在 ToolBar 上,
        不夠的話就顯示在菜單當(dāng)中-->

    <item
        android:id="@+id/delete"
        android:icon="@mipmap/ic_launcher"
        android:title="delete"
        app:showAsAction="ifRoom" />

    <!-- never 表示永遠(yuǎn)顯示在菜單當(dāng)中-->
    <item
        android:id="@+id/settings"
        android:icon="@mipmap/ic_launcher"
        android:title="settings"
        app:showAsAction="never" />
</menu>

注意:ToolBar 中的 action 按鈕只會(huì)顯示圖標(biāo),菜單中的 action 按鈕只會(huì)顯示文字。

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.backup:
                Toast.makeText(this, "backUp", Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete:
                Toast.makeText(this, "delete", Toast.LENGTH_SHORT).show();
                break;
            case R.id.settings:
                Toast.makeText(this, "settings", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
        return true;
    }

ToolBar 使用總結(jié):

    1. 首先要設(shè)置應(yīng)用主題是 NoActionBar 的
    1. 然后在布局文件中要使用 app 命名空間
    1. 給 ToolBar 設(shè)置高度為?attr/actionBarSizeActionBar 的高度,背景色設(shè)置為?attr/colorPirmary
    1. 根據(jù)程序主體顏色,設(shè)置 ToolBar 的 theme 主體顏色(與程序主體顏色相反),android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar",然后設(shè)置彈出的菜單主體顏色(與程序主體顏色相同)app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    1. ToolBar 最左側(cè)有一個(gè)按鈕,叫做 HomeAsUp,默認(rèn)是一個(gè)向左的箭頭,該按鈕的 id 永遠(yuǎn)都是 android.R.id.home

二、DrawerLayout(滑動(dòng)菜單)

??所謂滑動(dòng)菜單,就是將一些菜單隱藏起來,不放在主屏幕上,然后可以通過滑動(dòng)的方式將菜單顯示出來。我們可以借助 DrawerLayout 來實(shí)現(xiàn)這種效果。
??首先 DrawerLayout (support.v4庫提供)是一個(gè)布局,在布局中允許放入兩個(gè)直接子控件,第一個(gè)子控件是主屏幕中顯示的內(nèi)容,第二個(gè)控件是滑動(dòng)菜單中顯示的內(nèi)容(側(cè)邊欄)。

<?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">

    <!--  主屏幕的內(nèi)容 -->
    <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.v7.widget.Toolbar>

    </FrameLayout>

    <!-- 側(cè)邊欄的內(nèi)容 -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:text="我是側(cè)邊欄中的 TextView"
        android:textSize="30sp"
        android:background="@android:color/white"/>

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

注意:關(guān)于第二個(gè)子控件,layout_gravity這個(gè)屬性必須指定,因?yàn)槲覀冃枰嬖V DrawerLayout 滑動(dòng)菜單是在屏幕的左邊還是右邊,這里指定了 start,表示根據(jù)系統(tǒng)語言進(jìn)行判定,如果系統(tǒng)語言是從左往右的,比如英語、漢語,滑動(dòng)菜單就是在左邊,如果系統(tǒng)語言是從右往左的,比如阿拉伯語,滑動(dòng)菜單就是在右邊。

??這時(shí)候會(huì)有點(diǎn)問題,可能用戶不知道可以滑動(dòng),不知道有側(cè)邊欄這個(gè)東西,Material Design 建議的做法是在 Toolbar 的左邊加入一個(gè)導(dǎo)航按鈕,點(diǎn)擊按鈕就將側(cè)邊欄展示出來。

        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        /*
            通過 getSupportActionBar()方法獲取 ActionBar,雖然是 ActionBar,
            但是其實(shí)具體實(shí)現(xiàn)是由 ToolBar 來實(shí)現(xiàn)的
         */
        ActionBar actionBar = getSupportActionBar();
        /*
            這里注意,其實(shí) ToolBar 最左側(cè)的導(dǎo)航按鈕就叫做 HomeAsUp 按鈕
            他默認(rèn)的是一個(gè)返回箭頭,我們修改了他的樣式
         */
        if(actionBar != null){
            //讓導(dǎo)航按鈕顯示出來
            actionBar.setDisplayHomeAsUpEnabled(true);
            //設(shè)置導(dǎo)航按鈕圖標(biāo)
            actionBar.setHomeAsUpIndicator(R.mipmap.ic_launcher);
        }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.backup:
                Toast.makeText(this, "backUp", Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete:
                Toast.makeText(this, "delete", Toast.LENGTH_SHORT).show();
                break;
            case R.id.settings:
                Toast.makeText(this, "settings", Toast.LENGTH_SHORT).show();
                break;
            case android.R.id.home:
                /*
                    注意:這里,HomeAsUp 按鈕的 id 永遠(yuǎn)都是 android.R.id.home
                 */
                drawerLayout.openDrawer(GravityCompat.START);
                break;
            default:
                break;
        }
        return true;
    }

DrawerLayout 使用總結(jié):

    1. DrawerLayout 是一個(gè)布局,并且允許放入兩個(gè)直接子控件,第一個(gè)控件是主屏幕中顯示的內(nèi)容,第二個(gè)控件是滑動(dòng)菜單中顯示的內(nèi)容。
    1. 第二個(gè)控件必須制定 layout_gravity 屬性,用于告訴 DrawerLayout 是從哪個(gè)方向滑動(dòng)出來的。

三、NavigationView

??NavigationView 是 Design Support 庫中提供的一個(gè)控件,所以我們需要引入這個(gè)庫,而且在使用 NavigationView 之前,我們需要提前準(zhǔn)備好兩個(gè)東西:menu 和 headerLayout,menu 是用來在 NavigationView 中顯示具體的菜單項(xiàng)的,headerLayout 則是用來在 NavigationView 中顯示頭布局的。

    //design 庫
    compile 'com.android.support:design:24.2.1'
    //circleimageview
    compile 'de.hdodenhof:circleimageview:2.1.0'

??CircleImageView 可以用來輕松實(shí)現(xiàn)圖片圓形化的功能,項(xiàng)目主頁地址是:https://github.com/hdodenhof/CircleImageView
??在開始使用 NavigationView 之前,我們還需要提前準(zhǔn)備好兩個(gè)東西:menu 和 headerLayout,其中 menu 是用來在 NavigationView 中顯示具體的菜單項(xiàng)的, headerLayout 則是用來在顯示頭部布局的。
??接下來在 menu 文件夾中創(chuàng)建一個(gè) xml 文件,叫做nav_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- group表示一個(gè)組,checkableBehavior屬性指定為single表示組中所有菜單項(xiàng)只能單選 -->
    <group android:checkableBehavior="single">
        <item android:id="@+id/nav_call"
            android:icon="@mipmap/ic_launcher"
            android:title="Call" />

        <item android:id="@+id/nav_friends"
            android:icon="@mipmap/ic_launcher"
            android:title="Friends" />

        <item android:id="@+id/nav_location"
            android:icon="@mipmap/ic_launcher"
            android:title="Location" />

        <item android:id="@+id/nav_mail"
            android:icon="@mipmap/ic_launcher"
            android:title="Mail" />

        <item android:id="@+id/nav_task"
            android:icon="@mipmap/ic_launcher"
            android:title="Task" />
    </group>
</menu>

??然后我們?cè)?layout 文件夾中創(chuàng)建一個(gè) xml 文件,命名為:nav_header.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- 這里寬度設(shè)為match_parent,高度設(shè)為180dp,這是一個(gè) NavigationView 比較合適的高度 -->
<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"
    android:orientation="vertical">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/icon_img"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerInParent="true"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="www.baidu.com@game.com"
        android:textColor="#ffffff"
        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"
        android:textColor="#ffffff"
        android:textSize="14sp"/>

</RelativeLayout>

??然后修改 Activity 的 layout 布局文件

<?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">

    <!-- 主布局 -->
    <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.v7.widget.Toolbar>

    </FrameLayout>

    <!-- 側(cè)邊欄 -->
    <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:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_menu">
        <!-- 這里通過 app:header 和 app:menu 屬性將我們剛才準(zhǔn)備好的
             menu 和 headerLayout 設(shè)置拉進(jìn)去 -->

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

</android.support.v4.widget.DrawerLayout>
        navView = (NavigationView) findViewById(R.id.nav_view);
        //設(shè)置默認(rèn)選中的菜單項(xiàng)
        navView.setCheckedItem(R.id.nav_call);
        //菜單項(xiàng)選中事件監(jiān)聽
        navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                drawerLayout.closeDrawers();
                return true;
            }
        });
NavigationView.png

NavigationView使用總結(jié):

  • 1.使用 NavigationView 需要引入design 庫。
  • 2.還需要準(zhǔn)備兩個(gè) xml 文件:展示 menu 的 xml 文件,和展示頭部 header 的 xml 文件。
  • 3.頭部的 header 的 xml 布局文件中,最外層根布局高度設(shè)置為180dp,這是一個(gè)比較適合 NavigationView 的高度。
  • 4.然后通過 app:menu 和 app:headerLayout屬性來指定文件。

四、FloatingActionButton (懸浮按鈕)

??這是 Design Support 庫中提供的一個(gè)控件,默認(rèn)使用 colorAccent 來作為按鈕的顏色。

<?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">

    <!-- 主布局 -->
    <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.v7.widget.Toolbar>

        <!-- 懸浮按鈕 -->
        <!-- 這里 end 和 start 一樣,如果系統(tǒng)語言是從左往右的,那么 end
             就在右邊,如果系統(tǒng)語言是從右往左的,那么 end 就在左邊。
             可以使用 app:elevation 屬性來給按鈕指定一個(gè)高度值,高度值
             越大,投影范圍也越大,但是投影效果越淡,高度值越小,投影范圍
             也越小,投影效果越濃 -->
        <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="@mipmap/ic_launcher"
            app:elevation="8dp"/>

    </FrameLayout>

    <!-- 側(cè)邊欄 -->
    <!-- ...... -->

</android.support.v4.widget.DrawerLayout>
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(ThirdActivity.this, "點(diǎn)擊了懸浮按鈕", Toast.LENGTH_SHORT).show();
            }
        });

五、Snackbar

??還是由 Design Support 庫提供的。但是需要明確一點(diǎn),Snackbar 并不是 Toast 的替代品,他們兩者之間有著不同的應(yīng)用場(chǎng)景。Toast 的作用是告訴用戶現(xiàn)在發(fā)生了什么事情,用戶只能被動(dòng)接受。而 Snackbar 則在這方面進(jìn)行了擴(kuò)展,它允許在提示當(dāng)中加入一個(gè)可交互按鈕,當(dāng)用戶點(diǎn)擊按鈕的時(shí)候可以執(zhí)行一些額外的操作邏輯。

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                Toast.makeText(ThirdActivity.this, "點(diǎn)擊了懸浮按鈕", Toast.LENGTH_SHORT).show();
                //使用 Snackbar
                /*
                    Snackbar 使用 make 方法來創(chuàng)建
                        參1:傳入一個(gè) View 對(duì)象,只要是當(dāng)前界面任意一個(gè) View 都可以,Snackbar 會(huì)使用
                             這個(gè) View 來自動(dòng)查找最外層的布局,用于展示Snackbar
                        參2:Snackbar 中顯示的內(nèi)容
                        參3:Snackbar 顯示的時(shí)長(zhǎng)
                 */
                Snackbar.make(v,"是你點(diǎn)擊了懸浮按鈕?",Snackbar.LENGTH_SHORT)
                        //通過 setAction 方法設(shè)置一個(gè)動(dòng)作
                        .setAction("Yes", new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                Toast.makeText(ThirdActivity.this, "是我點(diǎn)擊了", Toast.LENGTH_SHORT).show();
                            }
                            //調(diào)用 show() 方法讓 Snackbar 顯示出來
                        }).show();
            }
        });
Snackbar.png

??不管是點(diǎn)擊 YES 還是過一段時(shí)間等待 Snackbar 自動(dòng)消失,Snackbar都是自帶動(dòng)畫效果的,但是你會(huì)發(fā)現(xiàn)一個(gè)問題,就是 Snackbar 將我們的 FloatingActionBar 給擋住了,這就需要借助 CoordinatorLayout 就行了。

六、CoordinatorLayout

??CoordinatorLayout 可以說是一個(gè)加強(qiáng)版的 FrameLayout,該布局也是由 Design Support 庫提供的,在普通情況下和 FrameLayout 基本一致。
??事實(shí)上,CoordinatorLayout 可以監(jiān)聽其所有子控件的各種事件,然后自動(dòng)幫我們做出最為合理的響應(yīng),舉個(gè)例子:剛才彈出的 Snackbar 將懸浮按鈕擋住了,如果我們讓 CoordinatorLayout 監(jiān)聽到 Snackbar 的彈出事件,那么它會(huì)自動(dòng)將內(nèi)部的 FloatingActionButton 向上偏移,從而確保不會(huì)被 Snackbar 擋住。

<?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.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.Toolbar>

        <!-- 懸浮按鈕 -->
        <!-- 這里 end 和 start 一樣,如果系統(tǒng)語言是從左往右的,那么 end
             就在右邊,如果系統(tǒng)語言是從右往左的,那么 end 就在左邊。
             可以使用 app:elevation 屬性來給按鈕指定一個(gè)高度值,高度值
             越大,投影范圍也越大,但是投影效果越淡,高度值越小,投影范圍
             也越小,投影效果越濃 -->
        <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="@mipmap/ic_launcher"
            app:elevation="8dp"/>

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

    <!-- 側(cè)邊欄 -->
    <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:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_menu">

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

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

CoordinatorLayout.png

??問題解決了,但是有點(diǎn)奇怪,F(xiàn)loatingActionButton 是 CoordinatorLayout 的子控件沒問題,但 Snackbar 并不是,為什么 CoordinatorLayout 還會(huì)監(jiān)聽到呢?

道理很簡(jiǎn)單,我們?cè)?Snackbar 的 make() 方法中傳入的第一個(gè)參數(shù),就是用來指定 Snackbar 是基于哪個(gè) View 來觸發(fā)的,我們傳入的是 FloatingActionBar,而 FloatingActionBar 又是 CoordinatorLayout 中的子控件,所以這個(gè)事件就能被監(jiān)聽到,如果給 Snackbar 的 make() 方法的第一個(gè)參數(shù)傳入別的 View,比如傳入 DrawerLayout,那么 Snackbar 就會(huì)再次遮擋懸浮按鈕,因?yàn)?DrawerLayout 不是 CoordinatorLayout 的子控件,CoordinatorLayout 也就無法堅(jiān)挺到 Snackbar 的彈出和隱藏事件了。

FloatingActionBar、Snackbar、CoordinatorLayout 使用總結(jié)

  • 1.這三個(gè)控件都是基于 Design Support 庫中的
  • 2.FloatingActionBar 可以通過 android:src 屬性設(shè)置圖標(biāo),還可以通過 app:elevation 設(shè)置按鈕懸浮高度,設(shè)置點(diǎn)擊事件和其他 View 的方法一樣
  • 3.CoordinatorLayout 其實(shí)就是高級(jí) FrameLayout,但是它可以監(jiān)聽其所有子控件的各種事件,然后自動(dòng)做出最為合理的響應(yīng)。
  • 4.Snackbar 通過 make 方法創(chuàng)建,通過setAction 方法設(shè)置一個(gè)動(dòng)作,可以和用戶進(jìn)行交互,通過 show 方法將 Snackbar 顯示出來。
  • 5.Snackbar 的 make 方法中,參1可以是當(dāng)前布局的任意一個(gè) View,如果有 FloatingActionBar 最好傳入 FloatingActionBar,然后和 CoordiantorLayout 一起使用,避免 Snackbar 擋住 FloatingActionBar。
  • 6.FloatingActionBar 可以通過 app:layout_anchor="@id/xxx" 屬性設(shè)置錨點(diǎn),指定懸浮按鈕出現(xiàn)在某個(gè)控件區(qū)域內(nèi),然后使用 app:layout_anchorGravity 屬性將懸浮按鈕定位在區(qū)域內(nèi)的某個(gè)位置。

七、CardView(卡片式布局)

??CardView 由 appcompat-v7 提供,實(shí)際上 CardView 也是一個(gè) FrameLayout,只是額外提供了圓角和陰影等效果,看上去會(huì)有立體的感覺。
??我們使用一個(gè)簡(jiǎn)單的例子來展示一下 CardView 控件

<?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.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.Toolbar>
        <!-- 這里添加一個(gè) RecyclerView -->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

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

        <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="@mipmap/ic_launcher"
            app:elevation="8dp"/>

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

    <!-- 側(cè)邊欄 -->
    <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:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_menu">

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

</android.support.v4.widget.DrawerLayout>
/**
 * 實(shí)體類
 */

public class Fruit {

    private String name;//水果的名字
    private int imageId;//水果對(duì)應(yīng)資源的圖片id

    public Fruit(){

    }

    public Fruit(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }

    @Override
    public String toString() {
        return "Fruit{" +
                "name='" + name + '\'' +
                ", imageId=" + imageId +
                '}';
    }
}
<!-- 這個(gè)是RecyclerView的item布局 -->
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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:layout_margin="5dp"
    android:orientation="vertical"
    app:cardCornerRadius="4dp">

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

        <ImageView
            android:id="@+id/iv_fruit"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_margin="5dp"
            android:textSize="16sp" />

    </LinearLayout>

</android.support.v7.widget.CardView>
/**
 * RecyclerView 的適配器
 */
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{

    private List<Fruit> mFruitList;
    private Context mContext;

    public FruitAdapter(List<Fruit> list){
        this.mFruitList = list;
    }

    static class ViewHolder extends RecyclerView.ViewHolder{

        ImageView ivFruit;
        TextView tvName;

        public ViewHolder(View itemView) {
            super(itemView);
            ivFruit = itemView.findViewById(R.id.iv_fruit);
            tvName = itemView.findViewById(R.id.tv_name);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(mContext == null){
            mContext = parent.getContext();
        }
        View view = LayoutInflater.from(mContext).inflate(R.layout.fruit_item,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.tvName.setText(fruit.getName());
        Glide.with(mContext).load(fruit.getImageId()).into(holder.ivFruit);
    }

    @Override
    public int getItemCount() {
        return mFruitList == null ? 0 : mFruitList.size();
    }
}

這里使用了 Glide 加載圖片,Glide 項(xiàng)目主頁地址是:https://github.com/bumptech/glide
這里為什么要用 Glide 而不用傳統(tǒng)的方式設(shè)置圖片呢?因?yàn)槿绻麍D片像素非常高的話,如果不進(jìn)行壓縮就直接展示,很容易就會(huì)引起內(nèi)存泄露,而 Glide 在內(nèi)部做了許多復(fù)雜的邏輯操作,其中就包括了圖片的壓縮。

    //數(shù)據(jù)
    private Fruit[] fruits = {
            new Fruit("Apple", R.mipmap.ic_launcher),
            new Fruit("Banana", R.mipmap.ic_launcher),
            new Fruit("Orange", R.mipmap.ic_launcher),
            new Fruit("Watermelon", R.mipmap.ic_launcher),
            new Fruit("Pear", R.mipmap.ic_launcher),
            new Fruit("Grape", R.mipmap.ic_launcher),
            new Fruit("Pineapple", R.mipmap.ic_launcher),
            new Fruit("Strawbeery", R.mipmap.ic_launcher),
            new Fruit("Cherry", R.mipmap.ic_launcher),
            new Fruit("Mango", R.mipmap.ic_launcher)
    };
    private List<Fruit> fruitList = new ArrayList<>();
        initFruits();

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this,2);
        recyclerView.setLayoutManager(gridLayoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    private void initFruits() {
        fruitList.clear();
        for (int i = 0; i < 50; i++) {
            Random random = new Random();
            int index = random.nextInt(fruits.length);
            fruitList.add(fruits[index]);
        }
    }
CardView.png

??CardView 的效果已經(jīng)出來了,但是你自己觀察一下,ToolBar 被 RecyclerView 擋住了,怎么辦?這就要借助另一個(gè)工具 -- AppBarLayout了。

八、AppBarLayout

??分析下上述問題的原因,因?yàn)?ToolBar 和 RecyclerView 都放在 CoordinatorLayout 中,而 CoordinatorLayout 是一個(gè)加強(qiáng)版的 FrameLayout,那么肯定會(huì)遮擋了啊。那么如果我們?cè)?CoordinatorLayout 內(nèi),給 ToolBar 和 RecyclerView 的包一層 LinearLayout 或者 RelativeLayout 是不是就可以了呢?是的,是可以了,但是,我們既然引出了 AppBarLayout,就有用 AppBarLayout 的理由。
??AppBarLayout 實(shí)際上是一個(gè)垂直方向上的 LinearLayout,而且它也是 Design Support 庫中的空間,它在其內(nèi)部做了非常多的滾動(dòng)事件的封裝。先看看如何使用:

    <!-- 主布局 -->
    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- 在 Toolbar 外層嵌套一個(gè) AppBarLayout -->
        <android.support.design.widget.AppBarLayout
            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"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

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

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

        <!-- 給 ReccylerView 添加一個(gè) app:layout_behavior 屬性,值也是固定這么寫的 -->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

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

        <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="@mipmap/ic_launcher"
            app:elevation="8dp" />

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

注意:這里如果將 RecyclerView 也放在 AppBarLayout 中,則會(huì)無法滑動(dòng)。如果不給 RecyclerView 添加 app:layout_behavior 屬性,則會(huì)出現(xiàn) RecyclerView 的上面部分被 ToolBar 遮蓋了,如下圖所示。

AppBarLayout2.png

??注意頂部,這個(gè)是 RecyclerView 滑動(dòng)到頂部的樣子,也就是說 RecyclerView 的上面部分被 ToolBar 遮蓋了。
??當(dāng) AppBarLayout 接收到滾動(dòng)事件的時(shí)候,它內(nèi)部的子控件其實(shí)是可以指定如何去響應(yīng)這些事件的,通過app:layout_scrollFlags屬性就可以

    <!-- 主布局 -->
    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- 在 Toolbar 外層嵌套一個(gè) AppBarLayout -->
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <!-- 給 Toolbar 添加了 app:layout_scrollFlags 屬性
                 其中 scroll 表示當(dāng) RecyclerView 向上滾動(dòng)的時(shí)候,Toolbar 會(huì)跟隨一起向上滾動(dòng)并實(shí)現(xiàn)隱藏;
                 enterAlways 表示當(dāng)RecyclerView 向下滾動(dòng)的時(shí)候,Toolbar 會(huì)跟著一起向下滾動(dòng)并重新顯示;
                 snap 表示當(dāng) Toolbar 還沒有完全隱藏或顯示的時(shí)候,會(huì)根據(jù)當(dāng)前滾動(dòng)的距離,自動(dòng)選擇是隱藏還是顯示-->
            <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"
                app:layout_scrollFlags="scroll|enterAlways|snap">

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

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

        <!-- 給 RecyclerView 添加一個(gè) app:layout_behavior 屬性,值也是固定這么寫的 -->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

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

        <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="@mipmap/ic_launcher"
            app:elevation="8dp" />

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

CardView、AppBarLayout使用總結(jié):

  • 1.CardView 是由 appcompat-v7 庫提供,實(shí)質(zhì)上是一個(gè) FrameLayout。AppBarLayout是由 Design Support 庫提供,實(shí)質(zhì)上是一個(gè)垂直方向上的 LinearLayout.
  • 2.CardView 中使用 app:cardCornerRadius 屬性指定卡片圓角的弧度,數(shù)值越大,圓角的弧度也越大,還可以使用 app:elevation 屬性指定卡片的高度,高度值越大,投影范圍越大,但是投影效果越淡。高度值越小,投影范圍也越小,但是投影效果越濃。
  • 3.解決 RecyclerView 覆蓋 Toolbar 問題,只需要兩步:第一步 --- 將 Toolbar 嵌套在 AppBarLayout 中;第二步 --- 給RecyclerView 指定一個(gè)布局行為:app:layout_behavior="@string/appbar_scrolling_view_behavior"
  • 4.讓 Toolbar 跟隨 RecyclerView 一起滑動(dòng)的效果:給 Toolbar 設(shè)置屬性app:layout_scrollFlags="scroll|enterAlways|snap"
    其中,scroll 表示 RecyclerView 上滑時(shí),Toolbar 會(huì)隱藏;enterAlways 表示 RecyclerView 下滑時(shí),Toolbar 會(huì)顯示;snap 表示 Toolbar 沒有完全隱藏或顯示的時(shí)候,會(huì)根據(jù)當(dāng)前滑動(dòng)的距離,自動(dòng)選擇隱藏還是顯示。

九、SwipeRefreshLayout(下拉刷新)

??SwipeRefreshLayout 是由 support-v4 庫提供,一般下拉刷新都配合 RecyclerView 一起使用,我們只需要在 RecyclerView 外層嵌套一個(gè) SwipeRefreshLayout 即可。

    <!-- 主布局 -->
    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- 在 Toolbar 外層嵌套一個(gè) AppBarLayout -->
        <android.support.design.widget.AppBarLayout
            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"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:layout_scrollFlags="scroll|enterAlways|snap"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

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

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

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

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

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

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

        <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="@mipmap/ic_launcher"
            app:elevation="8dp" />

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

注意:這里因?yàn)?RecyclerView 外面嵌套了一層 SwipeRefreshLayout 所以,之前設(shè)置的 app:layout_behavior 屬性也必須要放到 SwipeRefreshLayout 中才行。

        swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
        //設(shè)置下拉刷新進(jìn)度條的顏色,參數(shù)是可變參數(shù)
        swipeRefresh.setColorSchemeResources(R.color.colorPrimary,R.color.colorAccent);
        //設(shè)置下拉刷新的監(jiān)聽器,當(dāng)觸發(fā)了下拉刷新操作時(shí),就會(huì)回調(diào)這個(gè)監(jiān)聽器的 onRefresh 方法
        swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refreshFruits();
            }
        });
    private void refreshFruits(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        initFruits();
                        adapter.notifyDataSetChanged();
                        //傳入 false,表示刷新事件結(jié)束,并隱藏刷新進(jìn)度條
                        swipeRefresh.setRefreshing(false);
                    }
                });
            }
        }).start();
    }
swipeRefreshLayout.png

十、可折疊式標(biāo)題欄

??如果我們希望根據(jù)自己的喜好隨意定制標(biāo)題欄的樣式,比如實(shí)現(xiàn)一個(gè)可折疊式的標(biāo)題欄的效果,就需要借助 CollapsingToolbarLayout 這個(gè)工具。
??CollapsingToolbarLayout 是一個(gè)作用于 Toolbar 基礎(chǔ)之上的布局,它也是由 Design Support 庫提供,CollapsingToolbarLayout 可以讓 Toolbar 的效果變得更加豐富。

注意:CollapsingToolbarLayout 不能獨(dú)立存在,它在設(shè)計(jì)的時(shí)候就被限定只能作為AppBarLayout 的直接子布局來使用,而AppBarLayout 又必須是 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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="250dp">

        <!-- 嵌套在 AppBarLayout 中的 CollapsingToolbarLayout
              app:contentScrim 屬性用于指定 CollapsingToolbarLayout 在趨于折疊狀態(tài)
              以及折疊之后的背景色,其實(shí) CollapsingToolbarLayout 在折疊后就是一個(gè)普
              通的Toolbar。
              app:layout_scrollFlags 屬性中 scroll 表示 CollapsingToolbarLayout 會(huì)
              隨著內(nèi)容詳情的滾動(dòng)一起滾動(dòng),exitUntilCollapsed 表示當(dāng) CollapsingToolbarLayout
              會(huì)隨著完成折疊之后就保留在界面上,不再移出屏幕-->
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <!-- app:layout_collapseMode 屬性指定當(dāng)前控件在 CollapsingToolbarLayout
              折疊過程中的折疊模式,pin 表示在折疊過程中位置始終保持不變,parallax 表示
              會(huì)在折疊的過程中產(chǎn)生一定的錯(cuò)位偏移,視覺效果很好 -->
            <ImageView
                android:id="@+id/iv_fruit"
                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.v7.widget.Toolbar>

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

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

    <!-- NestedScrollView 是在 ScrollView 的基礎(chǔ)之上增加了嵌套
         響應(yīng)滾動(dòng)事件的功能,由于 CoordinatorLayout 本身已經(jīng)可以
         響應(yīng)滾動(dòng)事件了,因此我們?cè)趦?nèi)部就需要使用 NestedScrollView
         或 RecyclerView ,而且還加了 app:layout_behavior 屬性,
         為了不要遮擋Toolbar -->
    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

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

            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="15dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="35dp"
                app:cardCornerRadius="4dp">

                <TextView
                    android:id="@+id/tv_fruit_content"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dp" />

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

        </LinearLayout>

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

    <!-- 使用 app:layout_anchor 屬性指定一個(gè)錨點(diǎn)
         使用 app:layout_anchorGravity 屬性將懸浮按鈕定位在標(biāo)題欄區(qū)域的右下角 -->
    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|end"/>

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

該布局分三部分,最外層是一個(gè) CoordinatorLayout
第一部分:在 CoordinatorLayout 內(nèi)嵌一個(gè) AppBarLayout,然后在 AppBarLayout 中再內(nèi)嵌一個(gè) CollapsingToolbarLayout,然后再 CollapsingToolbarLayout 中再加上 ImageView 和 Toolbar 兩個(gè)控件。
第二部分:加入 NestedScrollView 控件,和 AppBarLayout 是同級(jí)的,在 NestedScrollView 內(nèi)部嵌套一個(gè) LinearLayout,然后在 LinearLayout 中加上 CardView 布局。
第三部分:比較簡(jiǎn)單,就是加一個(gè)FloatingActionButton,和 AppBarLayout 以及 NestedScrollView 都是同級(jí)的。

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
        collapsingToolbar.setTitle("Apple");

        ImageView ivFruit = (ImageView) findViewById(R.id.iv_fruit);
        TextView tvFruitContent = (TextView) findViewById(R.id.tv_fruit_content);
        Glide.with(this).load(R.mipmap.ic_launcher).into(ivFruit);
        tvFruitContent.setText(generateFruitContent("Apple"));
    /**
     * 生成比較長(zhǎng)的內(nèi)容
     */
    private String generateFruitContent(String name) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 500; i++) {
            sb.append(name);
        }
        return sb.toString();
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case android.R.id.home:
                finish();
                return true;
            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }
device-2018-04-17-150418.png

CollapsingToolbarLayout使用總結(jié):

  • 1.CollapsingToolbarLayout 是不能獨(dú)立存在的,必須作為 AppBarLayout 的直接子布局來使用,AppBarLayout 又必須是 CoordinatorLayout 的子布局。
  • 2.通過設(shè)置 app:contentScrim 屬性用于指定 CollapsingToolbarLayout 在趨于折疊狀態(tài)和折疊之后的背景色。還有 app:layout_scrollFlags 屬性,scroll 表示 CollapsingToolbarLayout 會(huì)隨之一起滾動(dòng),exitUntilCollapsed 表示當(dāng) CollapsingToolbarLayout 隨著滾動(dòng)完成折疊之后就保留在界面上,不再移出屏幕。
  • 3.CollapsingToolbarLayout 的子控件,可以添加 app:layout_collapseMode 屬性指定當(dāng)前控件在 CollapsingToolbarLayout 折疊過程中的折疊模式,pin 表示在折疊的過程中位置始終保持不變,parallax 表示會(huì)在折疊的過程中產(chǎn)生一定的錯(cuò)位偏移。

十一、系統(tǒng)狀態(tài)欄

??在 Android 5.0 系統(tǒng)之前,我們是無法對(duì)狀態(tài)欄的背景或顏色進(jìn)行操作的。

??想讓背景圖能夠和系統(tǒng)狀態(tài)欄融合,需要借助 android:fitsSystemWindows 這個(gè)屬性,在控件中,將該屬性指定為 true,表示該控件會(huì)出現(xiàn)在系統(tǒng)狀態(tài)欄里,修改布局文件:


image.png

這里如果只給 ImageView 設(shè)置 android:fitsSystemWindows 屬性是沒有用的,必須將 ImageView 布局結(jié)構(gòu)中的所有父布局都設(shè)置上這個(gè)屬性才可以。

image.png

??有點(diǎn)變化,但不是我們要的效果。這時(shí)就需要將狀態(tài)欄顏色指定成透明色才行。

設(shè)置成透明的方法很簡(jiǎn)單,在主題中將 android:statusBarColor 屬性的值指定成 @android:color/transparent 就可以了,但是問題是,android:statusBarColor 這個(gè)屬性是從 API 21(Android 5.0)開始才有的,之前的系統(tǒng)無法指定這個(gè)屬性。

??為了解決上述問題,我們做如下操作


image.png

??然后在 values-v21 目錄下創(chuàng)建一個(gè)styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="FruitActivityTheme" parent="AppTheme">
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
</resources>

由于 values-v21 目錄是只有 Android 5.0 及其以上的系統(tǒng)才會(huì)去讀取的,所以,這么寫是沒問題的。但是 Android 5.0 之前的系統(tǒng)卻無法識(shí)別 FruitActivityTheme 這個(gè)主題,因此我們還需要修改 values/styles.xml 文件。

    <!-- 因?yàn)锳ndroid 5.0 之前的系統(tǒng)無法指定狀態(tài)欄的顏色,所以這里什么都不用做 -->
    <style name="FruitActivityTheme" parent="AppTheme">

    </style>

??最后別忘了在清單文件中給 Activity 添加 Theme 屬性


image.png

最后,Material Design 的官方文章:https://material.google.com

下一篇文章:http://www.itdecent.cn/p/501825ee6fff

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,812評(píng)論 25 709
  • 本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):Toolbar滑動(dòng)菜單懸浮按鈕卡片式布局下拉刷新可折疊式標(biāo)題欄 Material ...
    開心wonderful閱讀 2,455評(píng)論 2 48
  • MaterialDesign是一套全新的界面設(shè)計(jì)語言,包含視覺,運(yùn)動(dòng),互動(dòng)效果等特性. Design Suppor...
    小徐andorid閱讀 1,099評(píng)論 0 9
  • CoordinatorLayout與滾動(dòng)的處理 CoordinatorLayout實(shí)現(xiàn)了多種Material De...
    cxm11閱讀 6,799評(píng)論 1 15
  • 1 我的大學(xué)生活之大學(xué)英語學(xué)習(xí) 1) 在這個(gè)學(xué)期的大學(xué)英語學(xué)習(xí)中,我的GPS [Gains] 1.收獲了一個(gè)語伴...
    1701段景昱閱讀 195評(píng)論 5 3

友情鏈接更多精彩內(nèi)容