12 (上) 最佳的UI體驗---Material Design實戰(zhàn)

本章的主要的知識點:

  • Toolbar
  • 滑動菜單
    1. DrawerLayout
    2. NavigationView
  • 懸浮按鈕和可交互提示
    1. FloatingActionButton
    2. Snackbar
    3. CoordinatorLayout
  • 卡片式布局
    1. CardView
    2. AppBarLayout
  • 下拉刷新
  • 可折疊式標(biāo)題欄
    1. CollapsingToolbarLayout
    2. 充分利用系統(tǒng)狀態(tài)欄空間
殺生丸.png

12.1 什么是Material Design

Material Design 是由谷歌的設(shè)計工程師們基于傳統(tǒng)優(yōu)秀的設(shè)計原則淆珊,結(jié)合豐富的創(chuàng)意和科學(xué)技術(shù)所發(fā)明的一套全新的界面設(shè)計語言勤晚,包含了視覺账磺,運動,互動效果等特性蔽豺。

谷歌從Android5.0系統(tǒng)開始,就將所有內(nèi)置的應(yīng)用都使用Material Design風(fēng)格來進(jìn)行設(shè)計悍募。

不過茧彤,在重磅推出之后蒜茴,Material Design的普及程度卻不能說是特別理想星爪。因為這只是一個推薦的設(shè)計規(guī)范,主要是面向UI設(shè)計人員的粉私,而不是面向開發(fā)者的顽腾。很多開發(fā)者可能就搞不清楚什么樣的界面和效果才叫Material Design,就算搞清楚了,實現(xiàn)起來也會很費勁诺核,因為不少Material Design 的效果是很難實現(xiàn)的抄肖。

2015年的Google I/O大會上推出了一個Design Support庫,這個庫將Material Design中最具代表性的一些控件和效果進(jìn)行了封裝窖杀,使得開發(fā)者在即使不了解Material Design的情況下也能非常輕松地將自己的應(yīng)用Material化漓摩。

12.2 Toolbar

不過ActionBar由于其設(shè)計的原因,被限定只能位于活動的頂部入客,從而不能實現(xiàn)一些Material Design的效果幌甘,因此官方現(xiàn)在已經(jīng)不再建議使用ActionBar了。

Toolar的強(qiáng)大之處在于痊项,它不僅繼承了ActionBar的所有功能锅风,而且靈活性很高,可以配合其它控件來完成一些Material Design的效果鞍泉。

任何一個新建的項目皱埠,默認(rèn)都會是顯示ActionBar的,那么這個ActionBar到底是從哪里來的呢咖驮?其實這是根據(jù)項目中指定的主題來顯示的边器,打開AndroidManifest.xml文件看一下

 <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

可以看到,這里使用android:theme屬性指定了一個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就是因為指定了這個主題才出現(xiàn)的砚嘴。

而現(xiàn)在我們準(zhǔn)備使用Toolbar替代ActionBar,因此需要指定一個不帶ActionBar的主題涩拙,通常有Theme.AppCompat.NoActionBarTheme.AppCompat.Light.NoActionBar這兩種主題可選际长。其中Theme.AppCompat.NoActionBar表示深色主題,它會將界面的主體顏色設(shè)成深色兴泥,配襯顏色設(shè)成單色工育。而Theme.AppCompat.Light.NoActionBar表示淡色主題,它會將界面的主體顏色設(shè)成淡色搓彻,配襯顏色設(shè)成深色如绸。

<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,colorPrimaryDarkcolorAccent這3個屬性的顏色。

各屬性指定的顏色位置

除了上述3個屬性之外怔接,我們還可以通過textColorPrimary,windowBackgroundnavigationBarColor等屬性來控制更多位置的顏色燥翅。不過唯獨colorAccent這個屬性比較難理解,它不只是用來指定這樣一個按鈕的顏色蜕提,而是更多表達(dá)了一個強(qiáng)調(diào)的意思森书,比如一些控件的選中狀態(tài)也會使用colorAccent的顏色。

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    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>

首先看一下第2行谎势,這里使用xmlns:app指定了一個新的命名空間凛膏。思考一下,正是由于每個布局文件都會使用xmlns:android來指定一個命名空間脏榆,因此我們才能一直使用android:id,android:layout_width等寫法猖毫。那么這里指定了xmlns:app,也就是說現(xiàn)在可以使用app:attribute這樣的寫法了须喂。由于Material Design是在Android5.0系統(tǒng)中才出現(xiàn)的吁断,而很多的Material屬性在5.0之前的系統(tǒng)中并不存在,那么為了能夠兼容之前的老系統(tǒng)我們就不能使用android:attribute這樣的寫法了坞生,而是應(yīng)該使用app:attribute仔役。

接下來定義了一個Toolbar控件,這個控件是由appcompat-v7庫提供的是己。這里我們給Toolbar指定了一個id又兵,將它的寬度設(shè)置為match_parent,高度設(shè)置為actionBar的高度,背景色設(shè)置為colorPrimary卒废。不過下面的部分就稍微有點難理解了沛厨。由于我們剛才在styles.xml中將程序的主題指定成了淡色主題,因此Toolbar現(xiàn)在也是淡色主題摔认,而Toolbar上面的各種元素就會自動使用深色系逆皮,這是為了和主題顏色區(qū)別開。但是這個效果看起來就會很差参袱,之前使用ActionBar時文字都是白色的电谣,現(xiàn)在變成黑色的會很難看。那么為了能讓Toolbar單獨使用深色主題蓖柔,這里我們使用android:theme屬性辰企,將Toolbar的主題指定成了ThemeOverlay.AppCompat.Dark.ActionBar风纠。但是這樣指定完了之后又會出現(xiàn)新的問題况鸣,如果Toolbar中有菜單按鈕,那么彈出的菜單項也會變成深色主題竹观。這樣就再次變得十分難看镐捧,于是這里使用了app:popupTheme屬性單獨將彈出的菜單項指定成了淡色主題潜索。之所以使用app:popupTheme,是因為popupTheme這個屬性是在Android5.0系統(tǒng)中新增的懂酱,我們使用app:popupTheme的話就可以兼容Android5.0以下的系統(tǒng)了竹习。

public class MainActivity extends AppCompatActivity
{

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

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

這里關(guān)鍵的代碼只有兩句,首先通過findViewById()得到Toolbar的實例列牺,然后調(diào)用setSupportActionBar()方法將Toolbar的實例傳入整陌,這樣我們就做到既使用了Toolbar,又讓它的外觀與功能和ActionBar一致了瞎领。

接下來我們再學(xué)習(xí)一些Toolbar比較常用的功能泌辫,比如修改標(biāo)題欄上顯示的文字內(nèi)容。這段文字內(nèi)容是在AndroidManifest.xml中指定的九默。

 <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:label="小吳">

這里給activity增加了一個android:label屬性震放,用于指定在Toolbar中顯示的文字內(nèi)容,如果沒有指定的話驼修,會默認(rèn)使用android:label="@string/app_name"指定的內(nèi)容殿遂,也就是我們的應(yīng)用名稱。

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

我們通過<item>標(biāo)簽來定義action按鈕乙各,android:id用于指定按鈕的id,android:icon用于指定按鈕的圖標(biāo)墨礁,android:title用于指定按鈕的文字。

接著使用app:showAsAction來指定按鈕的顯示位置耳峦,之所以這里再次使用了app命名空間饵溅,同樣是為了能夠兼容低版本的系統(tǒng)。showAsAction主要有以下幾種值可選:always表示永遠(yuǎn)顯示在Toolbar中妇萄,如果屏幕空間不夠則不顯示蜕企。ifRoom表示屏幕空間足夠的情況下顯示在Toolbar中,不夠的話就顯示在菜單當(dāng)中冠句。never則表示永遠(yuǎn)顯示在菜單當(dāng)中轻掩。注意Toolbar中的action按鈕只會顯示圖標(biāo),菜單中的action按鈕只會顯示文字懦底。

@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, "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:
                break;
        }
        return true;
    }

12.3 側(cè)滑菜單

側(cè)滑菜單可以說是Material Design中最常見的效果之一了唇牧。

12.3.1 DrawerLayout

所謂的側(cè)滑菜單就是將一些菜單選項隱藏起來,而不是放置在主屏幕上聚唐,然后可以通過滑動的方式將菜單顯示出來丐重。這種方式既節(jié)省了屏幕空間,又實現(xiàn)了非常好的動畫效果杆查,是Material Design中推薦的做法扮惦。

簡單介紹一下DrawerLayout的用法,首先它是一個布局亲桦,在布局中允許放入兩個直接子控件崖蜜,第一個子控件是主屏幕中顯示的內(nèi)容浊仆,第二個子控件是滑動菜單中顯示的內(nèi)容。

<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">
        </android.support.v7.widget.Toolbar>
    </FrameLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="This is menu"
        android:textSize="30sp"
        android:background="#FFF"
        android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>

這里最外層的控件使用了DrawerLayout豫领,這個控件是由support-v4庫提供的抡柿。DrawerLayout中放置了兩個直接子控件,第一個子控件是FrameLayout等恐,用于作為主屏幕中顯示的內(nèi)容洲劣,當(dāng)然里面還有我們剛剛定義的Toolbar。第二個子控件這里使用了一個TextView,用于作為滑動菜單中顯示的內(nèi)容课蔬,其實使用什么都可以闪檬,DrawerLayout并沒有限制只能使用固定的控件。

但是關(guān)于第二個子控件有一點需要注意购笆,layout_gravity這個屬性是必須要指定的粗悯,因為我們需要告訴DrawerLayout滑動菜單是在屏幕的左邊還是右邊,指定left表示滑動菜單在左邊同欠,指定right表示滑動菜單在右邊样傍。這里我制定了start,表示會根據(jù)系統(tǒng)語言去判斷铺遂,如果系統(tǒng)語言是從左往右的衫哥,比如英語,漢語襟锐,滑動菜單就在左邊撤逢,如果系統(tǒng)語言是從右往左的,比如阿拉伯語粮坞,滑動菜單就在右邊蚊荣。

想做滑動菜單,或者點擊一下菜單以外的區(qū)域莫杈,都可以讓滑動菜單關(guān)閉互例,從而回到主界面。無論是暫時還是隱藏滑動菜單筝闹,都是有非常流暢的動畫過渡的媳叨。

Material Design建議的做法是在Toolbar的最左邊加入一個導(dǎo)航按鈕,點擊了導(dǎo)航按鈕也會將滑動菜單的內(nèi)容展示出來关顷。這樣就相當(dāng)于給用戶提供了兩種打開滑動菜單的方式糊秆。

public class MainActivity extends AppCompatActivity
{

    private DrawerLayout mDrawerLayout;

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

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

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null)
        {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeAsUpIndicator(R.drawable.menu);
        }
    }

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

    @Override
    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:
                break;
        }
        return true;
    }
}

首先調(diào)用findViewById()方法得到了DrawerLayout的實例,然后調(diào)用getSupportActionBar()方法得到了ActionBar的實例议双,雖然這個ActionBar的具體實現(xiàn)是由Toolbar來完成的扛禽。接著調(diào)用ActionBarsetDisplayHomeAsUpEnabled()``方法讓導(dǎo)航按鈕顯示出來侥祭,又調(diào)用了setHomeAsUpIndicator()方法來設(shè)置一個導(dǎo)航按鈕圖標(biāo)通殃。實際上Toolbar最左側(cè)的這個按鈕就叫做HomeAsUp`按鈕,它默認(rèn)的圖標(biāo)是一個返回的箭頭界睁,含義是返回上一個活動觉增。很明顯兵拢,這里我們將它默認(rèn)的樣式和作用都進(jìn)行了修改。

接下來在onOptionsItemSelected()方法中對HomeAsUp按鈕的點擊事件進(jìn)行處理逾礁,HomeAsUp按鈕的id永遠(yuǎn)都是android.R.id.home说铃。然后調(diào)用DrawerLayoutopenDrawer()方法將滑動菜單顯示出來,注意openDrawer()方法要求傳入一個Gravity參數(shù)嘹履,為了保證這里的行為和XML中定義的一致腻扇,我們傳入了GravityCompat.START.

12.3.2 NavigationView

事實上,你可以在滑動菜單頁面定制任意的布局砾嫉,不過谷歌給我們提供了一種更好的方法---使用NavigationView幼苛。NavigationViewDesign Support庫中提供的一個控件,它不僅是嚴(yán)格按照Material Design的要求來進(jìn)行設(shè)計的焕刮,而且還可以將滑動菜單頁面的實現(xiàn)變得非常簡單舶沿。

既然這個控件是Design Support庫中提供的,那么我們就需要將這個庫引入到項目中才行配并。

compile 'com.android.support:design:25.1.1'
compile 'de.hdodenhof:circleimageview:2.1.0'

這里添加了兩行依賴關(guān)系括荡,第一行就是Design Support庫,第二行是一個開源項目CircleImageView,它可以用來輕松實現(xiàn)圖片圓形化的功能溉旋。

在開始使用NavigationView之前畸冲,我們還需要提前準(zhǔn)備好兩個東西:menuheaderLayoutmenu是用來在NavigationView中顯示具體的菜單項的观腊,headerLayout則是用來在NavigationView中顯示頭部布局的邑闲。

<?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="@drawable/call"
            android:title="Call"/>
        <item
            android:id="@+id/nav_friends"
            android:icon="@drawable/friends"
            android:title="Friends"/>
        <item
            android:id="@+id/nav_location"
            android:icon="@drawable/location"
            android:title="Location"/>
        <item
            android:id="@+id/nav_mail"
            android:icon="@drawable/mail"
            android:title="Mail"/>
        <item
            android:id="@+id/nav_task"
            android:icon="@drawable/tasks"
            android:title="Tasks"/>
    </group>

</menu>

我們首先在<menu>中嵌套了一個<group>標(biāo)簽,然后將group的checkableBehavior屬性指定為single梧油。group表示一個組监憎,checkableBehavior指定為single表示組中的所有菜單項只能單選。

這里一共定義了5個item婶溯,分別使用android:id屬性指定菜單項的id,android:icon屬性指定菜單項的圖標(biāo)鲸阔,android:title屬性指定菜單項顯示的文字。

接下來準(zhǔn)備headerLayout了迄委,這是一個可以隨意定制的布局褐筛。

<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_image"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@drawable/jinmu"
        android:layout_centerInParent="true"/>

    <TextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="wumeng1993@outlook.com"
        android:textColor="#FFF"
        android:textSize="14sp"/>

    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/username"
        android:text="WuMeng"
        android:textColor="#FFF"
        android:textSize="14sp"/>

</RelativeLayout>

布局文件的最外層是一個RelativeLayout,我們將它的寬度設(shè)為match_parent叙身,高度設(shè)為180dp,這是NavigationView比較適合的高度渔扎,然后指定它的背景色為colorPrimary.

<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">
        </android.support.v7.widget.Toolbar>
    </FrameLayout>

    <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.design.widget.NavigationView>

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


我們將之前的TextView換成了NavigationView,這樣滑動菜單中顯示的內(nèi)容也就變成了NavigationView了信轿,這里又通過app:menu和app:headerLayout屬性將我們剛才準(zhǔn)備好的menu和headerLayout設(shè)置了進(jìn)去晃痴,這樣NavigationView就定義好了残吩。

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

        ``
        NavigationView navView = (NavigationView) findViewById(R.id.nav_view);

        ``

        navView.setCheckedItem(R.id.nav_call);
        navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener()
        {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item)
            {
                switch (item.getItemId())
                {
                    case R.id.nav_call:
                        Toast.makeText(MainActivity.this, "Call", Toast.LENGTH_SHORT).show();
                        mDrawerLayout.closeDrawers();
                        break;
                    case R.id.nav_friends:
                        Toast.makeText(MainActivity.this, "Friends", Toast.LENGTH_SHORT).show();
                        mDrawerLayout.closeDrawers();
                        break;
                    case R.id.nav_location:
                        Toast.makeText(MainActivity.this, "Location", Toast.LENGTH_SHORT).show();
                        mDrawerLayout.closeDrawers();
                        break;
                    case R.id.nav_mail:
                        Toast.makeText(MainActivity.this, "Mail", Toast.LENGTH_SHORT).show();
                        mDrawerLayout.closeDrawers();
                        break;
                    case R.id.nav_task:
                        Toast.makeText(MainActivity.this, "Task", Toast.LENGTH_SHORT).show();
                        mDrawerLayout.closeDrawers();
                        break;
                }
                return true;
            }
        });
    }

    ``
}

這里首先獲取到了NavigationView的實例,然后調(diào)用它的setCheckedItem()方法將Call菜單設(shè)置為默認(rèn)選中倘核,接著調(diào)用了setNavigationItemSelectedListener()方法來設(shè)置一個菜單項選中事件的監(jiān)聽器泣侮,當(dāng)用戶點擊了任意的菜單項時。就會回調(diào)到onNavigationItemSelected()方法中紧唱。我們可以在這個方法中寫相應(yīng)的邏輯處理活尊。

12.4 懸浮按鈕和可交互提示

立面設(shè)計是Material Design中一條非常重要的設(shè)計思想,也就是說漏益,按照Material Design的理念蛹锰,應(yīng)用程序的界面不僅僅是一個平面,而應(yīng)該是有立體效果的绰疤。在官方給出的示例中铜犬,最簡單且最具代表性的立面設(shè)計就是懸浮按鈕了,這種按鈕不屬于主界面平面的一部分轻庆,而是位于另外一個維度的癣猾,因此就會給人一種懸浮的感覺。

FloatingActionButton

FloatingActionButtonDesign Support庫中提供的一個控件榨了,這個控件可以幫助我們比較輕松里實現(xiàn)懸浮按鈕的效果煎谍。它默認(rèn)會使用colorAccent來作為按鈕的顏色,我們還可以通過給按鈕指定一個圖標(biāo)來表明這個按鈕的作用是什么龙屉。

<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">
        </android.support.v7.widget.Toolbar>

        <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="@drawable/done"/>
    </FrameLayout>

    ``

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


這里我們在主屏幕布局中加入了一個FloatingActionButton呐粘。layout_gravity屬性指定將這個控件放置于屏幕的右下角,其中end的工作原理和之前的start是一樣的转捕,即如果系統(tǒng)語言是從左往右的作岖,那么end就表示在右邊,如果系統(tǒng)語言是從右往左的五芝,那么end就表示在左邊痘儡。

如果你仔細(xì)觀察的話,會發(fā)現(xiàn)這個懸浮按鈕的下面還有一點陰影枢步。其實這很好理解沉删,因為FloatingActionButton是懸浮在當(dāng)前界面上的,既然是懸浮醉途,那么就理所應(yīng)當(dāng)會有投影矾瑰,Design Support庫連這種細(xì)節(jié)都幫我們考慮到了。

說道懸浮隘擎,其實我們還可以指定FloatingActionButton的懸浮高度殴穴。

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

這里使用app:elevation屬性來給app:elevation指定一個高度值,高度值越大,投影范圍也越大采幌,但是投影效果越淡劲够,高度值越小,投影范圍也越小休傍,但是投影效果越濃征绎。當(dāng)然這些效果的差異其實都不怎么明顯,我個人感覺使用默認(rèn)的FloatingActionButton效果就已經(jīng)足夠了尊残。

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        fab.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                Toast.makeText(MainActivity.this, "fab clicked", Toast.LENGTH_SHORT).show();
            }
        });

FloatingActionButton的點擊事件就沒什么好說的了炒瘸。

12.4.2 Snackbar

Snackbar 并不是Toast的替代品淤堵,它們兩者之間有著不同的應(yīng)用場景寝衫。Toast的作用是告訴用戶現(xiàn)在發(fā)生了什么事情,但同時用戶只能被動接受這個事情拐邪,因為沒有什么辦法能讓用戶進(jìn)行選擇慰毅。而Snackbar則在這方面進(jìn)行了擴(kuò)展,它允許在提示當(dāng)中加入一個可交互按鈕扎阶,當(dāng)用戶點擊按鈕的時候可以執(zhí)行一些額外的邏輯操作汹胃。

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

這里調(diào)用了Snackbar的make()方法來創(chuàng)建一個Snackbar對象,make()方法的第一個參數(shù)需要傳入一個View东臀,只要是當(dāng)前界面布局的任意一個View都可以着饥,Snackbar會使用這個View來自動查找最外層的布局,用于展示Snackbar惰赋。第二個參數(shù)就是Snackbar中顯示的內(nèi)容宰掉,第三個參數(shù)是Snackbar顯示的時長。

接著這里用調(diào)用了一個setAction()方法來設(shè)置一個動作赁濒,從而讓Snackbar不僅僅是一個提示轨奄,而是可以和用戶進(jìn)行交互的。

12.4.3 CoordinatorLayout (協(xié)調(diào)員布局)

CoordinatorLayout可以說是一個加強(qiáng)版的FrameLayout拒炎,這個布局也是由Design Support庫提供的挪拟。它在普通情況下的作用和FrameLayout基本一致。

  • 當(dāng)做普通的FrameLayout作為根布局使用
  • 作為一個或者多個子View進(jìn)行復(fù)雜交互的容器

CoordinatorLayout為我們提供了一個叫做Behavior的東西击你,我們基本上的復(fù)雜交互都是使用Behavior來協(xié)調(diào)完成玉组。

事實上,CoordinatorLayout可以監(jiān)聽其所有子控件的各種事件丁侄,然后自動幫助我們作出最為合理的解釋惯雳。

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

        <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="@drawable/done"
            app:elevation="8dp"/>
    </android.support.design.widget.CoordinatorLayout>

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

剛才說的是CoordinatorLayout可以監(jiān)聽其所有子控件的各種事件鸵钝,但是Snackbar并不是CoordinatorLayout的子控件糙臼,為什么它卻可以被監(jiān)聽到呢?

其實道理很簡單恩商,還記得我們在Snackbarmake()方法中傳入的第一個參數(shù)嗎变逃?這個參數(shù)就是用來指定Snackbar是基于哪個View來觸發(fā)的,剛才我們傳入的是FloatingActionButton本身怠堪,而FloatingActionButtonCoordinatorLayout中的子控件揽乱,因此這個事件就理所應(yīng)當(dāng)能被監(jiān)聽到了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粟矿,一起剝皮案震驚了整個濱河市凰棉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陌粹,老刑警劉巖撒犀,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異掏秩,居然都是意外死亡或舞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門蒙幻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來映凳,“玉大人,你說我怎么就攤上這事邮破≌┩悖” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵决乎,是天一觀的道長队询。 經(jīng)常有香客問我,道長构诚,這世上最難降的妖魔是什么蚌斩? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮范嘱,結(jié)果婚禮上送膳,老公的妹妹穿的比我還像新娘。我一直安慰自己丑蛤,他們只是感情好叠聋,可當(dāng)我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著受裹,像睡著了一般碌补。 火紅的嫁衣襯著肌膚如雪虏束。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天厦章,我揣著相機(jī)與錄音镇匀,去河邊找鬼。 笑死袜啃,一個胖子當(dāng)著我的面吹牛汗侵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播群发,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼晰韵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了熟妓?” 一聲冷哼從身側(cè)響起雪猪,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滑蚯,沒想到半個月后浪蹂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抵栈,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡告材,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了古劲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斥赋。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖产艾,靈堂內(nèi)的尸體忽然破棺而出疤剑,到底是詐尸還是另有隱情,我是刑警寧澤闷堡,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布隘膘,位于F島的核電站,受9級特大地震影響杠览,放射性物質(zhì)發(fā)生泄漏弯菊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一踱阿、第九天 我趴在偏房一處隱蔽的房頂上張望管钳。 院中可真熱鬧,春花似錦软舌、人聲如沸才漆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽醇滥。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸳玩,已是汗流浹背焰手。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留怀喉,地道東北人书妻。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像躬拢,于是被迫代替她去往敵國和親躲履。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容