譯者地址:【翻】Android Design Support Library 的 代碼實(shí)驗(yàn)——幾行代碼成黄,讓你的 APP 變得花俏
原文項(xiàng)目 demo: Lab-Android-DesignLibrary
雙語對(duì)照地址: 【翻-雙語】Android Design Support Library 的 代碼實(shí)驗(yàn)——幾行代碼休弃,讓你的 APP 變得花俏
目前银受,我相信践盼,沒有任何 Android 開發(fā)者不知道材料設(shè)計(jì)的,因?yàn)樗脑O(shè)計(jì)在過去的一年震驚了世界宾巍,正式的變成了一個(gè)設(shè)計(jì)理念咕幻。
令人驚訝的是,在 Android 應(yīng)用中材料設(shè)計(jì)是不容易實(shí)現(xiàn)的顶霞,因?yàn)椴牧显O(shè)計(jì)的 UI 組件 如: Floating Action Button (FAB) 在低于 Android L 系統(tǒng)上是不可用的肄程。我們只能選擇使用由獨(dú)立開發(fā)者公布出來的第三方庫。
來了一個(gè)好消息选浑,上周(2015.5.29)在谷歌2015 I/O 大會(huì)時(shí)蓝厌,谷歌宣布了一個(gè)今年最讓人興奮的支持庫,名叫 Android Design Support Library古徒,在這個(gè)單獨(dú)的 library 里提供了一堆有用的材料設(shè)計(jì) UI 組件拓提。通過這篇文章,讓我用這個(gè)機(jī)會(huì)向你一個(gè)一個(gè)描述如何來使用他們隧膘。
請(qǐng)查看下面這個(gè)視頻作為本教程最終的結(jié)果代态。
![0](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/0.gif)
從這里開始,空白 Activity 里面有一個(gè) DrawerLayout 疹吃。
![1](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/1.gif)
Activity 已經(jīng)調(diào)整為材料設(shè)計(jì)風(fēng)格的主題蹦疑。
<item name="colorPrimary">#2196F3</item>
<item name="colorPrimaryDark">#1565C0</item>
<item name="colorAccent">#E91E63</item>
好了,讓我們開始吧互墓!
步驟一:從 Github 上拷貝源碼
我已經(jīng)為這個(gè) codelab 準(zhǔn)備了源碼必尼,你可以從 GitHub 輕松的 clone 它篡撵。MainActivity 是上面所示的最終結(jié)果。請(qǐng)?jiān)谶@個(gè) project 的 CodeLabActivity
中做我們的代碼實(shí)驗(yàn)育谬。
你一定要自己做的一個(gè)任務(wù)是... 成功的運(yùn)行它,它應(yīng)該是通過簡(jiǎn)單的點(diǎn)擊“運(yùn)行”按鈕來完成锰镀。
步驟二:添加 Android Design Support Library 依賴
第一件要做的事是在我們的項(xiàng)目中添加 Android Design Support Library,在 app 的 build.gradle
文件下添加一行依賴代碼憾筏。
compile 'com.android.support:design:22.2.0'
`
請(qǐng)注意 Design Support Library 依賴于 Support v4 和 AppCompat v7花鹅。一旦你在你的項(xiàng)目中添加這個(gè) library,你也將獲得一個(gè)這些 libraries 的組件的入口古拴。(譯者注:就是說 Design Support Library 中就已經(jīng)包含了 Support v4 和 AppCompat v7)
順便說一下,從 Github 克隆的源碼已經(jīng)添加了上面這行代碼真友。但是如果你創(chuàng)建了你自己的項(xiàng)目黄痪,你需要自己添加它。
步驟三:添加 FAB
Floating Action Button (FAB) 是一個(gè)有一些陰影的圓形按鈕盔然,這個(gè)令人難以置信的桅打,可以改變世界的設(shè)計(jì)。毫不奇怪它為什么會(huì)變成材料設(shè)計(jì)的標(biāo)志轻纪。因此我們從這開始油额。添加一個(gè) FAB 在布局文件叠纷,因?yàn)樗枰恍└割悂硎顾谄聊坏挠蚁路轿恢脤?duì)齊刻帚,所以用 FrameLayout
來包裹 FloatingActionButton
。請(qǐng)做這樣的事情作為 DrawerLayout 的內(nèi)容:更換 activity_code_lab.xml
中已經(jīng)存在的 TextView
涩嚣,像下面的代碼這樣崇众。
<android.support.v4.widget.DrawerLayout ...
xmlns:app="http://schemas.android.com/apk/res-auto"
....>
<FrameLayout
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/fabBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:src="@drawable/ic_plus"
app:fabSize="normal" />
</FrameLayout>
...
</android.support.v4.widget.DrawerLayout>
android:src
是用來定義你想要的資源文件 ID(推薦 40dp 的清晰的 png 文件),而 app:fabSize="normal"
是用來定義 FAB 的大小的航厚,normal
的意思是在大多數(shù)情況下標(biāo)準(zhǔn)尺寸為 56dp 的按鈕顷歌,但是萬一你想使用較小的一個(gè), mini
是另一個(gè)選擇幔睬,它的大小將變成 40dp眯漩。
就這樣麻顶,F(xiàn)AB 現(xiàn)在準(zhǔn)備使用辅肾!下面是當(dāng)我在 Android 4.4 上運(yùn)行這段代碼的結(jié)果矫钓。
![p0](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/0.jpg)
但是當(dāng)我們運(yùn)行在 Android 5.0 上時(shí),結(jié)果變成了這樣...
![p1](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/1.jpg)
這不是特效既绩,只是一個(gè) bug熬词。幸運(yùn)的是 design library 的開發(fā)者團(tuán)隊(duì)已經(jīng)知道這個(gè)問題并在不久的將來會(huì)發(fā)布一個(gè)修復(fù)的版本互拾。但是如果你現(xiàn)在想要使用它颜矿,我們可以做一些事情:通過設(shè)置 FAB 的 margin right 和 margin bottom 為 16dp 在 API Level 21+ 上面并在 低于 Android L 的版本上 設(shè)置為 0dp骑疆。感謝配置資源可以讓我們非常容易的做到這一點(diǎn)箍铭。
res/values/dimens.xml
<dimen name="codelab_fab_margin_right">0dp</dimen>
<dimen name="codelab_fab_margin_bottom">0dp</dimen>
res/values-v21/dimens.xml
<dimen name="codelab_fab_margin_right">16dp</dimen>
<dimen name="codelab_fab_margin_bottom">16dp</dimen>
res/layout/activity_code_lab.xml
<android.support.design.widget.FloatingActionButton
...
android:layout_marginBottom="@dimen/codelab_fab_margin_bottom"
android:layout_marginRight="@dimen/codelab_fab_margin_right"
.../>
好了诈火!
![p2](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/2.jpg)
這里有另一個(gè) bug冷守。陰影,你在哪里馆截?這個(gè) bug 和先前的那個(gè)是有關(guān)聯(lián)的混卵。你可以通過定義 app:borderWidth="0"
作為 FAB 的屬性 作為一個(gè)快速的解決方案淮菠。
![p3](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/3.jpg)
歡迎回來合陵,陰影拥知!其深度是自動(dòng)設(shè)置的最佳實(shí)踐之一:6dp 在空閑狀態(tài)低剔,12dp 是按下狀態(tài)姻锁。反正你可以通過定義重寫這些值位隶,app:elevation
為空閑狀態(tài)下的陰影深度涧黄,andapp:pressedTranslationZ
為按下狀態(tài)的笋妥。
關(guān)于按鈕的顏色春宣,F(xiàn)AB 基本上使用強(qiáng)調(diào)色信认,但是你可以重寫 app:backgroundTint
屬性來修改。
就像傳統(tǒng)的按鈕油挥,你可以通過 setOnClickListener()
處理點(diǎn)擊深寥,在 CodeLabActivity.java
文件的 initInstances
方法中添加下面的代碼惋鹅。
FloatingActionButton fabBtn;
...
private void initInstances() {
...
fabBtn = (FloatingActionButton) findViewById(R.id.fabBtn);
fabBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
完成闰集!
步驟四:使用 Snackbar
Snackbar武鲁,在屏幕的地步一個(gè)微小的黑色條顯示著一條簡(jiǎn)短的消息挚瘟,在這個(gè) library 中也是可用的乘盖。Snackbar 和 Toast 有著相同的概念侧漓,但是不同于 Toast布蔗,它的表現(xiàn)是作為 UI 的一部分而不是覆蓋在屏幕上纵揍。
![p4](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/4.jpg)
不只是概念相同泽谨,編碼風(fēng)格也是由 Toast 所啟發(fā),你可以通過下面的代碼喚起 Snackbar涂身。
Snackbar.make(someView, "Hello. I am Snackbar!", Snackbar.LENGTH_SHORT)
.setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
})
.show();
make()
的第一個(gè)參數(shù)是一個(gè) View 或者 Layout丁鹉,你想在它的底部位置顯示一個(gè) Snackbar揣钦。在這個(gè)例子中冯凹,一個(gè) FrameLayout 包裹著一個(gè) FAB 就是其中一個(gè)例子宇姚。setAction()
方法是用在設(shè)置動(dòng)作顯示在 Snackbar 的右側(cè)并有對(duì)應(yīng)的監(jiān)聽嚎花。這個(gè)方法不是必需的紊选,可以移除兵罢。
現(xiàn)在卖词,讓我們通過添加下面的代碼去試試此蜈。
FrameLayout rootLayout;
...
private void initInstances() {
...
rootLayout = (FrameLayout) findViewById(R.id.rootLayout);
fabBtn = (FloatingActionButton) findViewById(R.id.fabBtn);
fabBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Snackbar.make(rootLayout, "Hello. I am Snackbar!", Snackbar.LENGTH_SHORT)
.setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
})
.show();
}
});
}
點(diǎn)擊 FAB 以及看到的結(jié)果。
![p5](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/5.jpg)
有用战授!但是... 還不是很完美植兰。它是出現(xiàn)在放置 Snackbar 頂部的位置楣导,長(zhǎng)期的用戶體驗(yàn)是很差的爷辙。不管怎么樣,這個(gè)行為已經(jīng)是正確的务冕,因?yàn)檫@里沒有為 Snackbar 和 FAB 定義任何關(guān)聯(lián)禀忆。
為了這個(gè)目的專門發(fā)明了一個(gè)特殊的布局离熏,使子 Views 協(xié)調(diào)工作滋戳。這就不用奇怪為什么它的名字是 CoordinatorLayout
了奸鸯。
步驟五:使他們和 CoordinatorLayout 協(xié)作
CoordinatorLayout 是一個(gè)讓子 Views 協(xié)調(diào)工作的布局娄涩。這里沒有任何魔法蓄拣。每個(gè) View 中肯定是設(shè)計(jì)和實(shí)現(xiàn)了和 CoordinatorLayout 協(xié)同工作的弯蚜。FAB 和 Snackbar 就是這兩個(gè)view碎捺。
所以... 現(xiàn)在讓我們將 FrameLayout 改成 CoordinatorLayout
包裹一個(gè)FAB。
res/layout/activity_code_lab.xml
<android.support.design.widget.CoordinatorLayout
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
... />
</android.support.design.widget.CoordinatorLayout>
而且诵叁,不要忘了在 CodeLabActivity.java
改變 rootLayout 的變量類型為 CoordinatorLayout拧额,否則就會(huì)崩潰侥锦。
//FrameLayout rootLayout;
CoordinatorLayout rootLayout;
//rootLayout = (FrameLayout) findViewById(R.id.rootLayout);
rootLayout = (CoordinatorLayout) findViewById(R.id.rootLayout);
結(jié)果:現(xiàn)在 FAB 隨著 Snackbar 的出現(xiàn)和消失而移動(dòng)恭垦。還增加了一些功能唠帝。Snackbar 現(xiàn)在能夠滑動(dòng)消失了襟衰!請(qǐng)?jiān)囈辉嚒?/p>
![2](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/2.gif)
但是 bug 到處都是… bug 出現(xiàn)在低于 Android L 的系統(tǒng)上瀑晒,當(dāng) Snackbar 滑動(dòng)消失的時(shí)候,F(xiàn)AB 忘記了移動(dòng)下來映砖。
![3](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/3.gif)
這顯然是一個(gè) bug竹宋,但是我不知道確切的原因心褐。感謝天主莫矗,這里有一些解決方法三娩。從我的實(shí)驗(yàn)中雀监,我發(fā)現(xiàn)當(dāng)我們?cè)O(shè)置 FAB 的 margin bottom 和 margin right 為一些非零的正數(shù)值時(shí)会前,它將會(huì)奇跡般的正常工作瓦宜,所以..就只需要為低于 Android L 的系統(tǒng)改變 margin 的值為 0.1dp就行歉提。
res/values/dimens.xml
<dimen name="codelab_fab_margin_right">0.1dp</dimen>
<dimen name="codelab_fab_margin_bottom">0.1dp</dimen>
完成苔巨。下面是結(jié)果侄泽。
![4](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/4.gif)
從現(xiàn)在起悼尾,如果你計(jì)劃使用 Android Design Support Library闺魏。請(qǐng)首先考慮 CoordinatorLayout,因?yàn)樗拖袷沁@個(gè) library 的核心艰垂。
步驟六:再見 ActionBar,你好娩怎,Toolbar
Toolbar 不是 Android Design Support Library 的一部分截亦,而是在這個(gè)庫中需要與其他組件一起使用崩瓤。
Toolbar 是一個(gè)替代傳統(tǒng)的 Action Bar 具有更靈活的行為。我鼓勵(lì)你們從現(xiàn)在開始隱藏 Action Bar 并且切換到 Toolbar肾扰。因?yàn)檫@些有奇妙功能的新庫集晚,包括 Design Support Library 的組件中偷拔,都被設(shè)計(jì)為和 Toolbar 協(xié)同工作而不是 Action Bar欺旧。
很容易切換到 Toolbar辞友。只需要從 Activity 定義的 AppTheme 的 style 屬性隱藏掉 Action Bar 開始称龙。
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
然后在 CoordinatorLayout 里面的 FAB 之前正確的放一個(gè) Toolbar 組件鲫尊。
<android.support.design.widget.CoordinatorLayout
...>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<android.support.design.widget.FloatingActionButton
...>
</android.support.design.widget.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>
現(xiàn)在寫代碼來告訴系統(tǒng),我們將使用 Toolbar 作為一個(gè) Action Bar扛施,更換下面的 Java 代碼疙渣。
Toolbar toolbar;
private void initInstances() {
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
...
}
雖然它現(xiàn)在可以運(yùn)行成功泼菌,但是根據(jù)我之前說的哗伯,放在 CoordinatorLayout 的東西必須被設(shè)計(jì)和實(shí)現(xiàn)成與它一起合作的焊刹,否則將不與任何其他兄弟 views(sibling views) 協(xié)作虐块。但是... Toolbar是不合適的贺奠。別擔(dān)心挂据,這里沒有任何新的特殊 Toolbar崎逃。只是一個(gè)組件是為了準(zhǔn)備讓 Toolbar 與 CoordinatorLayout 一起工作的更加完美婚脱。這是簡(jiǎn)單的任務(wù),只是簡(jiǎn)單的用 AppBarLayout
包裹 Toolbar吟宦,就這樣殃姓!
<android.support.design.widget.CoordinatorLayout
...>
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
.../>
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.FloatingActionButton
...>
</android.support.design.widget.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>
現(xiàn)在運(yùn)行和測(cè)試蜗侈,如果你做的都是對(duì)的踏幻,你將會(huì)看到 Drawer Menu 會(huì)覆蓋在 App Bar區(qū)域的頂部该面。使用了 AppBarLayout 的輸出結(jié)果是:低于應(yīng)用欄區(qū)域的陰影現(xiàn)在回來了隔缀,耶!(譯者注:不曉得怎么翻了:The outgrowth of applying AppBarLayout is the drop shadow below App Bar area is now returned ! Yah !)
![5](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/5.gif)
這個(gè)步驟現(xiàn)在完成了牵触。從現(xiàn)在開始荒吏,我建議你總是用 AppBarLayout 包裹 ToolBar 元素绰更。光憑它能帶回來陰影的能力就足夠有說服力儡湾。
步驟7:在內(nèi)容區(qū)域放東西
我們已經(jīng)得到了 FAB 和 Toolbar徐钠,現(xiàn)在是時(shí)候在 Activity 的內(nèi)容區(qū)域放上東西了尝丐。
額远荠。如果是兩個(gè)簡(jiǎn)單的按鈕呢譬淳?好吧邻梆,讓我們把它們放在在 AppBarLayout 和 FAB 之間浦妄。
...
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Yo Yo"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Yo Yo"/>
</LinearLayout>
<android.support.design.widget.FloatingActionButton
...>
下面是結(jié)果...
![p6](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/6.jpg)
這些按鈕似乎都出人意料的放在了 Toolbar 下面。猜猜為什么宜咒?
是的故黑,一些古老的原因场晶,LinearLayout 沒有被設(shè)計(jì)成與 CoordinatorLayout 協(xié)同工作诗轻。在這樣的情況下扳炬,沒有任何布局用來包裹 LinearLayout恨樟,使它像 Toolbar 的做法那樣劝术。但它是更加容易的衬吆,你只需要在 LinearLayout 添加一個(gè)屬性告訴它的滾動(dòng)行為咆槽,就像下面寫的這樣:
<LinearLayout
...
app:layout_behavior="@string/appbar_scrolling_view_behavior"
...>
現(xiàn)在,他們被放在了正確的位置了蛾娶,耶蛔琅!
![p7](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/7.jpg)
完成罗售!=)
步驟8:玩轉(zhuǎn) TabLayout
Tab 是在 Android 應(yīng)用程序中用戶體驗(yàn)(UX)最佳實(shí)踐的一部分寨躁。在以前,如果我們想要使用新的材料設(shè)計(jì)風(fēng)格的 Tab方面,我們需要自己去為項(xiàng)目中下載 SlidingTabLayout 和 SlidingTabStrip 的源碼〔儋鳎現(xiàn)在颓屑,我們只需要使用這個(gè)庫提供的 TabLayout
邢锯,它也有很多可以調(diào)整的選項(xiàng)丹擎。
我們應(yīng)該把 TabLayout 放在哪里蒂培?根據(jù) Android 應(yīng)用程序用戶體驗(yàn)指導(dǎo)原則翎冲,Tab 應(yīng)該放在屏幕的頂部而不是在底部抗悍。還有缴渊,它應(yīng)該在陰影部分的上面衔沼。所以指蚁,我們將其放在 AppBarLayout 里面凝化,沿著 Toolbar。這是可以做到的袜蚕,因?yàn)?AppBarLayout 是繼承自一個(gè)垂直的 LinearLayout遣疯。
<android.support.design.widget.AppBarLayout ...>
<android.support.v7.widget.Toolbar ... />
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.design.widget.AppBarLayout>
在 Java 代碼中添加一些 tabs缠犀。
TabLayout tabLayout;
private void initInstances() {
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
...
}
下面是結(jié)果:
![p8](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/8.jpg)
背景色會(huì)自動(dòng)設(shè)置成 primary color(主題色)虐急,而導(dǎo)航線的顏色是強(qiáng)調(diào)色止吁。但是你將會(huì)注意到 Tab 的字體仍然是黑色的敬惦,但是我們希望字體是白色的俄删。這是因?yàn)槲覀冞€沒有為 TabLayout 提供任何主題呢畴椰。TabLayout 定義主題是簡(jiǎn)單的迅矛,就像這樣:
<android.support.design.widget.TabLayout
...
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
現(xiàn)在,他們是白色的了威兜。
![p9](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/9.jpg)
你可以像上面這樣選擇手動(dòng)控制 TabLayout椒舵,或者讓它和 ViewPager 一起工作笔宿,自動(dòng)調(diào)用 setupWithViewPager(...)
。我相信這種情況會(huì)很頻繁的使用迈勋。
還有重归,我們可以調(diào)整兩個(gè)屬性來顯示 TabLayout。
app:tabMode
- 如果你想在屏幕上顯示出每個(gè)單獨(dú)的 tab育苟,就設(shè)置 tab 為 fixed
的, 拓哺。它適合只有少數(shù) tab 的時(shí)候闲孤,但是如果有很多的 tab 的時(shí)候這是一個(gè)完全錯(cuò)誤的選擇讼积。在這種情況下你是不確定所有的 tab 是否能很好的在同一時(shí)間顯示出來的勤众。所以们颜,你可以設(shè)置這個(gè)屬性為 scrollable
讓用戶去滾動(dòng) tab窥突,就像 Google Play Store 那樣。
app:tabGravity
- 如果你想要分配所有的可用空間給每個(gè) tab沦疾,就設(shè)置這個(gè)屬性為 fill
刨秆。如果你想要所有的 tab 在屏幕的中間坛善,就設(shè)置這個(gè)屬性為 center
剔交。請(qǐng)注意岖常,如果 tabMode 是設(shè)置成 scrollable 的竭鞍,則這個(gè)屬性將會(huì)被忽略偎快。
每個(gè)模式的樣子就像下面這樣:
![p10](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/10.jpg)
TabLayout 完成了!
步驟9:當(dāng)隨著內(nèi)容滾動(dòng)時(shí)丐怯,讓 AppBarLayout 退出屏幕
一個(gè)優(yōu)美的 Android 用戶體驗(yàn)是引導(dǎo) App Bar 可以隨著內(nèi)容滾動(dòng)出屏幕的读跷,以獲得額外的空間來顯示內(nèi)容效览,并且钦铺,這已經(jīng)是被證明這樣的用戶體驗(yàn)是很棒的。以前有一些應(yīng)用程序已經(jīng)實(shí)現(xiàn)了這種行為烫映,但是開發(fā)者必須自己來實(shí)現(xiàn)。現(xiàn)在它只需要用一行代碼就能輕松的完成识补。
首先祝辣,我們需要讓內(nèi)容能夠滾動(dòng)蝙斜,先往 LinearLayout 加入一些 Button孕荠。大約20個(gè)弯予?
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Yo Yo"/>
...
<!-- Add 20 more buttons here -->
...
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Yo Yo"/>
然后用 ScrollView 包裹這個(gè) LinearLayout锈嫩,還有祠挫,不要忘了將 LinearLayout 里的 layout_behavior 移動(dòng)到 ScrollView等舔,因?yàn)楝F(xiàn)在 ScrollView 是 CoordinatorLayout的最直接的子 view慌植。
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
</LinearLayout>
</ScrollView>
然后給 Toolbar 添加一個(gè)滾動(dòng)標(biāo)志,就像這樣:
<android.support.v7.widget.Toolbar
...
app:layout_scrollFlags="scroll|enterAlways" />
試試吧
![6](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/6.gif)
額... 原先假定的 Toolbar 會(huì)隨著內(nèi)容的滾動(dòng)滾出屏幕的交汤,但是為什么它看起來什么都沒有實(shí)現(xiàn)呢芙扎?
同樣的老原因啦... ScrollView 沒有被設(shè)計(jì)成與 CoordinatorLayout 協(xié)同工作(又來)戒洼。你需要另一個(gè) view:NestedScrollView
圈浇,Android Support Library v4 中有提供磷蜀。這個(gè) NestedScrollView 設(shè)計(jì)出來的目的就是為了與 CoordinatorLayout 協(xié)同工作的怎茫。
<android.support.v4.widget.NestedScrollView ...>
<LinearLayout ...>
...
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
同樣的原因轨蛤,請(qǐng)注意了: ListView 類也是和 CoordinatorLayout 不能協(xié)同工作的祥山。只有 RecyclerView
可以缝呕。也許需要時(shí)間來改變咯~
這里將 ScrollView 改變成 NestedScrollView 后的結(jié)果供常。
![7](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/7.gif)
運(yùn)行起來真贊!你會(huì)注意到 Toolbar 滾出了屏幕源祈,但是 TabLayout 仍然還在香缺。這是因?yàn)槲覀儧]有給 TabLayout 設(shè)置任何滾動(dòng)標(biāo)志图张。如果你想要 TabLayout 同樣從屏幕上消失埂淮,只需要給 TabLayout 定義相同的屬性就可以了。
<android.support.design.widget.TabLayout
...
app:layout_scrollFlags="scroll|enterAlways" />
結(jié)果:
![8](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/8.gif)
這里會(huì)有一些手勢(shì)上的 bug慕趴。我發(fā)現(xiàn)拉它回到屏幕是非常困難的冕房“也幔看來我們得等下一個(gè)版本了帝际。
現(xiàn)在蹲诀,讓我們來看看它的一些細(xì)節(jié)脯爪。很好奇這些標(biāo)志的真實(shí)意思是什么:scroll
和 enterAlways
痕慢?事實(shí)上我們可以在這里設(shè)置4個(gè)屬性值。
scroll
- 你想你想要設(shè)置這個(gè) view 隨著內(nèi)容滾動(dòng)拇泛,你需要應(yīng)用這個(gè)標(biāo)志俺叭。
enterAlwaysCollapsed
- 這個(gè)標(biāo)志定義了 View 是如何回到屏幕的。當(dāng)你的 view 已經(jīng)聲明了一個(gè)最小高度(minHeight) 并且你使用了這個(gè)標(biāo)志裕照,你的 View 只有在回到這個(gè)最小的高度的時(shí)候才會(huì)展開晋南,只有當(dāng) view 已經(jīng)到達(dá)頂部之后它才會(huì)重新展開全部高度负间。滾動(dòng)標(biāo)志像這樣來使用它:scroll|enterAlwaysCollapsed
政溃。
![9](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/9.gif)
它好像在這個(gè) minHeight 部分死活不工作扼鞋。這里和 TabLayout 有另一個(gè)問題云头。很難把這些 View 拉回到屏幕來盘寡。
enterAlways
- 這個(gè)標(biāo)志確保了任何向下滾動(dòng)的操作都會(huì)讓這個(gè) view 變得可見竿痰,達(dá)到“快速返回”(‘quick return’ )的效果影涉,滾動(dòng)標(biāo)志像這樣來使用它: scroll|enterAlways
![10](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/10.gif)
exitUntilCollapsed
- View 將關(guān)閉滾動(dòng)直到它被折疊起來(有 minHeight) 并且一直保持這樣,舉個(gè)例子:
<android.support.v7.widget.Toolbar
...
android:layout_height="192dp"
android:gravity="bottom"
android:paddingBottom="12dp"
android:minHeight="?attr/actionBarSize"
app:layout_scrollFlags="scroll|exitUntilCollapsed"/>
下面是上述代碼的結(jié)果:
![11](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/11.gif)
這種模式在組件中經(jīng)常使用,我將在下一個(gè)部分討論豁陆。
步驟10: 移除 TabLayout
從實(shí)驗(yàn)來看盒音,在上述情況下當(dāng)我們用 TabLayout 來滾動(dòng)的時(shí)候,有一些明顯的 bug雄坪。我相信這只是一個(gè) bug诸衔,而且以后會(huì)被修復(fù)的。現(xiàn)在谒亦,讓我們首先從代碼中移除 TabLayout份招,確保下一步運(yùn)行是流暢的。
<!--android.support.design.widget.TabLayout -->
從 Java 代碼中也刪除
//tabLayout = (TabLayout) findViewById(R.id.tabLayout);
//tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
//tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
//tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
好了谐腰,讓我們?nèi)プ鱿乱徊剑?/p>
Step 11: Make Toolbar collapsable 步驟11:使工具欄可折疊
就像在 exitUntilCollapsed 部分所示的例子中,Toolbar 可以展開和折疊砸西,但是你會(huì)看到它還不是很完美芹枷。Toolbar 仍然離開了屏幕,最好的體驗(yàn)是讓這些 icon (漢堡等-即菜單欄) 應(yīng)該留在屏幕內(nèi)蝶涩。
Design Support Library 已經(jīng)為這個(gè)準(zhǔn)備好了。用 CollapsingToolbarLayout
你可以像魔術(shù)一樣讓 Toolbar 折疊起來熄攘,就像其他組件一樣挪圾,它是非常容易使用的洼畅,具體操作步驟如下:
用
CollapsingToolbarLayout
包裹Toolbar
棚赔,但仍然在AppBarLayout
中從
Toolbar
中刪除layout_scrollFlags
為
CollapsingToolbarLayout
聲明layout_scrollFlags
帝簇,并且將layout_scrollFlags
設(shè)置成scroll|exitUntilCollapsed
改變 AppBarLayout 擴(kuò)張狀態(tài)時(shí)的布局高度大小。在這個(gè)例子中靠益,我用 256dp
這是最終代碼丧肴。
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="256dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
這個(gè)結(jié)果是:
![12](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/12.gif)
看起來不錯(cuò),但是這些 Toolbar icons 仍然滾出了屏幕胧后。我們可以聲明這個(gè)屬性給 Toolbar 來固定住它芋浮,讓它總是在屏幕的頂部何暇。
<android.support.v7.widget.Toolbar
...
app:layout_collapseMode="pin"/>
Toolbar現(xiàn)在被定住了!
![13](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/13.gif)
但是,等一下…標(biāo)題的文字在哪里?!不幸的是艰管,在用 CollapsingToolbarLayout 包裹住 Toolbar 后缸浦,它隨風(fēng)而逝了。我們必須通過在 Java 代碼中手動(dòng)設(shè)置 setTitle(String)
來實(shí)現(xiàn)。
CollapsingToolbarLayout collapsingToolbarLayout;
private void initInstances() {
...
collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
collapsingToolbarLayout.setTitle("Design Library");
}
結(jié)果:
![14](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/14.gif)
這里的字體顏色仍然是黑的的铃绒。這是因?yàn)槲覀冞€沒有為 App Ba 設(shè)置任何主題。要做到這一點(diǎn),只需要簡(jiǎn)單的為 AppBarLayout
聲明 android:theme
屬性就可以了,就像這樣:
<android.support.design.widget.AppBarLayout
...
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
現(xiàn)在,標(biāo)題變成了白色的了啥纸!
![15](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/15.gif)
由于CollapsingToolbarLayout 的 特點(diǎn)荣暮,應(yīng)用的標(biāo)題文字在收縮和展開狀態(tài)是會(huì)自動(dòng)過渡的。如果你想要在展開狀態(tài)改變標(biāo)題文字的位置,你可以這樣做:通過應(yīng)用的 margin 的4個(gè)屬性,就是:app:expandedTitleMargin
, app:expandedTitleMarginBottom
, app:expandedTitleMarginEnd
以及 app:expandedTitleMarginStart
或者如果你想要在折疊和展開狀態(tài)時(shí)改變文本的顯示。你可以這樣來簡(jiǎn)單的實(shí)現(xiàn):設(shè)置 TextAppearance,分別通過 app:collapsedTitleTextAppearance
和 app:expandedTitleTextAppearance
來設(shè)置。
讓我們從試著改變 margin 為64dp 開始。
<android.support.design.widget.CollapsingToolbarLayout
...
app:expandedTitleMarginStart="64dp">
結(jié)果:
![16](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/16.gif)
真棒!
步驟12:為 App Bar 添加背景圖片
在這種情況下,我們想要用一張美麗的圖片作為 App Bar 的背景,而不只是像現(xiàn)在這樣的一個(gè)普通的顏色。幸運(yùn)的是 CollapsingToolbarLayout 是繼承自 FrameLayout 所以我們可以輕松的添加一個(gè) ImageView 作為 Toolbar 的背景圖層,就像這樣:
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/header" />
<android.support.v7.widget.Toolbar
...
結(jié)果:
![17](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/17.gif)
圖片已經(jīng)顯示出來了帖烘,但是這里有一點(diǎn)還沒有達(dá)到預(yù)期,藍(lán)色的導(dǎo)航條仍舊顯示著。有一個(gè) Toolbar 的背景看起來不是酷炫的。從 Toolbar 移除它,只需要下面這行代碼就行了。
android:background="?attr/colorPrimary"
結(jié)果:
![18](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/18.gif)
現(xiàn)在圖片是隨著內(nèi)容的滾動(dòng)了摹恨,但是看起來太呆了寝凌。我們可以使用視差模式讓它變得更優(yōu)雅一些青柄,只需要聲明 collapse 就行了虹蒋,像下面這樣:
<ImageView
...
app:layout_collapseMode="parallax" />
結(jié)果:
![19](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/19.gif)
你也可以設(shè)置視差的系數(shù)邪驮,介于 0.0-1.0之間喻粹。
app:layout_collapseParallaxMultiplier="0.7"
請(qǐng)你自己去嘗試一下=)
最后你可能會(huì)注意到 App Bar 的背景總顯示一張圖片查乒。你可以讓它在收縮的時(shí)候自動(dòng)的變化到普通的顏色,通過聲明屬性 app:contentScrim 像下面這樣來實(shí)現(xiàn):
<android.support.design.widget.CollapsingToolbarLayout
...
app:contentScrim="?attr/colorPrimary">
結(jié)果:
![20](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/20.gif)
只用了幾行代碼瘸彤,就讓 App Bar 變得這么漂亮了 =)
步驟13:玩轉(zhuǎn) Navigation Drawer
現(xiàn)在從左側(cè)拉出 Drawer Menu 仍然只是一個(gè)空白的面板拯杠。在以前,實(shí)現(xiàn)這個(gè)菜單是非常麻煩的枝秤,因?yàn)槲覀儾坏貌皇謩?dòng)的用 LinearLayout 或者 ListView 去實(shí)現(xiàn)菌赖。
在 Android Design Support Library 中提供了 NavigationView,實(shí)現(xiàn)它變得更容易了到逊,它為我們節(jié)省了15.84321倍的時(shí)間铜靶!
首先,為 Drawer Menu 創(chuàng)建一個(gè)標(biāo)題視頻布局文件嚼吞。(它已經(jīng)在 Github的項(xiàng)目中了)
res/layout/nav_header.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="192dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/nav_header_bg"
android:scaleType="centerCrop" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/nuuneoi"
android:layout_gravity="bottom"
android:layout_marginBottom="36dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_margin="16dp"
android:text="nuuneoi"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
</FrameLayout>
現(xiàn)在創(chuàng)建一個(gè)菜單資源文件
res/menu/navigation_drawer_items.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="all">
<item
android:id="@+id/navItem1"
android:icon="@drawable/ic_action_location_found_dark"
android:title="Home"/>
<item
android:id="@+id/navItem2"
android:icon="@drawable/ic_action_location_found_dark"
android:title="Blog"/>
<item
android:id="@+id/navItem3"
android:icon="@drawable/ic_action_location_found_dark"
android:title="About"/>
<item
android:id="@+id/navItem4"
android:icon="@drawable/ic_action_location_found_dark"
android:title="Contact"/>
</group>
</menu>
NavigationView
與兩個(gè)資源文件綁定起來俏脊,作為 Drawer Menu 的菜單區(qū)域漫萄,用下面的代碼來替換一個(gè)已經(jīng)存在的 白色的 LinearLayout :
...
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:itemIconTint="#333"
app:itemTextColor="#333"
app:menu="@menu/navigation_drawer_items" />
</android.support.v4.widget.DrawerLayout>
現(xiàn)在:召喚 Drawer Menu启昧!哇喔,哇喔
![21](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/21.gif)
NavigationView 就是為了 Drawer Menu 而特別設(shè)計(jì)的座柱。所以景用,所有的東西都會(huì)被創(chuàng)建并且自動(dòng)測(cè)量包括菜單的寬度等舀瓢,我們自己定義案例來配置以前的設(shè)計(jì)。
為了處理這些菜單項(xiàng)的點(diǎn)擊事件赶袄,你可以聲明 setNavigationItemSelectedListener
來監(jiān)聽颜说,就像下面這樣:
NavigationView navigation;
private void initInstances() {
...
navigation = (NavigationView) findViewById(R.id.navigation);
navigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
int id = menuItem.getItemId();
switch (id) {
case R.id.navItem1:
break;
case R.id.navItem2:
break;
case R.id.navItem3:
break;
}
return false;
}
});
}
在實(shí)際使用中乾吻,請(qǐng)隨意的區(qū)聲明你想要定義的 header view 和修改菜單項(xiàng)。
步驟14:用上 TextInputLayout 讓 EditText 變的更風(fēng)騷
這是 Codelab 的最后一部分了扭勉。你可以改變一個(gè)舊的 EditText 的風(fēng)格,讓它變得更時(shí)髦,即:總是會(huì)顯示一個(gè)提示或者一個(gè)錯(cuò)誤信息遣鼓。
要做到這一點(diǎn),只需要簡(jiǎn)單的用 TextInputLayout 包裹住一個(gè) EditText 堵第,就這么簡(jiǎn)單也搓!
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Username" />
</android.support.design.widget.TextInputLayout>
把這兩個(gè)控件放到 NestedScrollView 里看下結(jié)果。
![22](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/gif/22.gif)
難以置信的容易吧宇挫?=)
結(jié)論
Android Design Support Library 是非常有前途的支持庫欣除,它非常值得在你的產(chǎn)品上使用墨辛。雖然它仍然包含了很多錯(cuò)誤磨淌,我建議你再等等,直到每個(gè)錯(cuò)誤都被修復(fù)堵幽。
這么長(zhǎng)的教程,希望希望你覺得它有用 =)
`
![p11](https://raw.githubusercontent.com/MrFuFuFu/Codelab/master/pic/11.png)
Author: nuuneoi (Android GDE, CTO & CEO at The Cheese Factory)
A full-stack developer with more than 6 years experience on Android Application Development and more than 12 years in Mobile Application Development industry. Also has skill in Infrastucture, Service Side, Design, UI&UX, Hardware, Optimization, Cooking, Photographing, Blogging, Training, Public Speaking and do love to share things to people in the world!