本章的主要的知識點:
Toolbar
- 滑動菜單
DrawerLayout
NavigationView
- 懸浮按鈕和可交互提示
FloatingActionButton
Snackbar
CoordinatorLayout
- 卡片式布局
CardView
AppBarLayout
- 下拉刷新
- 可折疊式標(biāo)題欄
CollapsingToolbarLayout
- 充分利用系統(tǒng)狀態(tài)欄空間
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.NoActionBar
和Theme.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
,colorPrimaryDark
和colorAccent
這3個屬性的顏色。
除了上述3個屬性之外怔接,我們還可以通過textColorPrimary
,windowBackground
和navigationBarColor
等屬性來控制更多位置的顏色燥翅。不過唯獨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)用ActionBar
的setDisplayHomeAsUpEnabled()``方法讓導(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)用DrawerLayout
的openDrawer()
方法將滑動菜單顯示出來,注意openDrawer()
方法要求傳入一個Gravity
參數(shù)嘹履,為了保證這里的行為和XML
中定義的一致腻扇,我們傳入了GravityCompat.START
.
12.3.2 NavigationView
事實上,你可以在滑動菜單頁面定制任意的布局砾嫉,不過谷歌給我們提供了一種更好的方法---使用NavigationView
幼苛。NavigationView
是Design 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)備好兩個東西:menu
和headerLayout
。menu
是用來在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
FloatingActionButton
是Design 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)聽到呢?
其實道理很簡單恩商,還記得我們在Snackbar
的make()
方法中傳入的第一個參數(shù)嗎变逃?這個參數(shù)就是用來指定Snackbar
是基于哪個View
來觸發(fā)的,剛才我們傳入的是FloatingActionButton
本身怠堪,而FloatingActionButton
是CoordinatorLayout
中的子控件揽乱,因此這個事件就理所應(yīng)當(dāng)能被監(jiān)聽到了。