譯者地址:【翻】Android Design Support Library 的 代碼實驗——幾行代碼,讓你的 APP 變得花俏
原文項目 demo: Lab-Android-DesignLibrary
雙語對照地址: 【翻-雙語】Android Design Support Library 的 代碼實驗——幾行代碼,讓你的 APP 變得花俏
目前,我相信,沒有任何 Android 開發者不知道材料設計的,因爲它的設計在過去的一年震驚了世界,正式的變成了一個設計理念。
令人驚訝的是,在 Android 應用中材料設計是不容易實現的,因爲材料設計的 UI 組件 如: Floating Action Button (FAB) 在低於 Android L 系統上是不可用的。我們只能選擇使用由獨立開發者公佈出來的第三方庫。
來了一個好消息,上週(2015.5.29)在谷歌2015 I/O 大會時,谷歌宣佈了一個今年最讓人興奮的支持庫,名叫 Android Design Support Library,在這個單獨的 library 裏提供了一堆有用的材料設計 UI 組件。通過這篇文章,讓我用這個機會向你一個一個描述如何來使用他們。
請查看下面這個視頻作爲本教程最終的結果。
從這裏開始,空白 Activity 裏面有一個 DrawerLayout 。
Activity 已經調整爲材料設計風格的主題。
1
2
3
|
< item name = "colorPrimary" >#2196F3</ item > < item name = "colorPrimaryDark" >#1565C0</ item > < item name = "colorAccent" >#E91E63</ item > |
好了,讓我們開始吧!
步驟一:從 Github 上拷貝源碼
我已經爲這個 codelab 準備了源碼,你可以從 GitHub 輕鬆的 clone 它。MainActivity 是上面所示的最終結果。請在這個 project 的CodeLabActivity中做我們的代碼實驗。
你一定要自己做的一個任務是... 成功的運行它,它應該是通過簡單的點擊“運行”按鈕來完成。
步驟二:添加 Android Design Support Library 依賴
第一件要做的事是在我們的項目中添加 Android Design Support Library,在 app 的build.gradle文件下添加一行依賴代碼。
1
|
compile
'com.android.support:design:22.2.0' |
`
請注意 Design Support Library 依賴於 Support v4 和 AppCompat v7。一旦你在你的項目中添加這個 library,你也將獲得一個這些 libraries 的組件的入口。(譯者注:就是說 Design Support Library 中就已經包含了 Support v4 和 AppCompat v7)
順便說一下,從 Github 克隆的源碼已經添加了上面這行代碼。但是如果你創建了你自己的項目,你需要自己添加它。
步驟三:添加 FAB
Floating Action Button (FAB) 是一個有一些陰影的圓形按鈕,這個令人難以置信的,可以改變世界的設計。毫不奇怪它爲什麼會變成材料設計的標誌。因此我們從這開始。添加一個 FAB 在佈局文件,因爲它需要一些父類來使它在屏幕的右下方位置對齊,所以用FrameLayout來包裹FloatingActionButton。請做這樣的事情作爲 DrawerLayout 的內容:更換activity_code_lab.xml中已經存在的TextView,像下面的代碼這樣。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< 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的意思是在大多數情況下標準尺寸爲 56dp 的按鈕,但是萬一你想使用較小的一個,mini是另一個選擇,它的大小將變成 40dp。
就這樣,FAB 現在準備使用!下面是當我在 Android 4.4 上運行這段代碼的結果。
但是當我們運行在 Android 5.0 上時,結果變成了這樣...
這不是特效,只是一個 bug。幸運的是 design library 的開發者團隊已經知道這個問題並在不久的將來會發佈一個修復的版本。但是如果你現在想要使用它,我們可以做一些事情:通過設置 FAB 的 margin right 和 margin bottom 爲 16dp 在 API Level 21+ 上面並在 低於 Android L 的版本上 設置爲 0dp。感謝配置資源可以讓我們非常容易的做到這一點。
res/values/dimens.xml
1
2
|
< dimen name = "codelab_fab_margin_right" >0dp</ dimen > < dimen name = "codelab_fab_margin_bottom" >0dp</ dimen > |
res/values-v21/dimens.xml
1
2
|
< dimen name = "codelab_fab_margin_right" >16dp</ dimen > < dimen name = "codelab_fab_margin_bottom" >16dp</ dimen > |
res/layout/activity_code_lab.xml
1
2
3
4
5
|
< android.support.design.widget.FloatingActionButton ... android:layout_marginBottom = "@dimen/codelab_fab_margin_bottom" android:layout_marginRight = "@dimen/codelab_fab_margin_right" .../> |
好了!
這裏有另一個 bug。陰影,你在哪裏?這個 bug 和先前的那個是有關聯的。你可以通過定義app:borderWidth="0"作爲 FAB 的屬性 作爲一個快速的解決方案。
歡迎回來,陰影!其深度是自動設置的最佳實踐之一:6dp 在空閒狀態,12dp 是按下狀態。反正你可以通過定義重寫這些值,app:elevation爲空閒狀態下的陰影深度,andapp:pressedTranslationZ爲按下狀態的。
關於按鈕的顏色,FAB 基本上使用強調色,但是你可以重寫app:backgroundTint屬性來修改。
就像傳統的按鈕,你可以通過setOnClickListener()處理點擊,在CodeLabActivity.java文件的initInstances方法中添加下面的代碼。
1
2
3
4
5
6
7
8
9
10
11
|
FloatingActionButton
fabBtn; ... private void
initInstances() { ... fabBtn
= (FloatingActionButton) findViewById(R.id.fabBtn); fabBtn.setOnClickListener( new View.OnClickListener()
{ @Override public void
onClick(View v) { } }); } |
完成!
步驟四:使用 Snackbar
Snackbar,在屏幕的地步一個微小的黑色條顯示着一條簡短的消息,在這個 library 中也是可用的。Snackbar 和 Toast 有着相同的概念,但是不同於 Toast,它的表現是作爲 UI 的一部分而不是覆蓋在屏幕上。
不只是概念相同,編碼風格也是由 Toast 所啓發,你可以通過下面的代碼喚起 Snackbar。
1
2
3
4
5
6
7
|
Snackbar.make(someView, "Hello.
I am Snackbar!" ,
Snackbar.LENGTH_SHORT) .setAction( "Undo" , new View.OnClickListener()
{ @Override public void
onClick(View v) { } }) .show(); |
make()的第一個參數是一個 View 或者 Layout,你想在它的底部位置顯示一個 Snackbar。在這個例子中,一個 FrameLayout 包裹着一個 FAB 就是其中一個例子。setAction()方法是用在設置動作顯示在 Snackbar 的右側並有對應的監聽。這個方法不是必需的,可以移除。
現在,讓我們通過添加下面的代碼去試試。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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(); } }); } |
點擊 FAB 以及看到的結果。
有用!但是... 還不是很完美。它是出現在放置 Snackbar 頂部的位置,長期的用戶體驗是很差的。不管怎麼樣,這個行爲已經是正確的,因爲這裏沒有爲 Snackbar 和 FAB 定義任何關聯。
爲了這個目的專門發明了一個特殊的佈局,使子 Views 協調工作。這就不用奇怪爲什麼它的名字是CoordinatorLayout了。
步驟五:使他們和 CoordinatorLayout 協作
CoordinatorLayout 是一個讓子 Views 協調工作的佈局。這裏沒有任何魔法。每個 View 中肯定是設計和實現了和 CoordinatorLayout 協同工作的。FAB 和 Snackbar 就是這兩個view。
所以... 現在讓我們將 FrameLayout 改成CoordinatorLayout包裹一個FAB。
res/layout/activity_code_lab.xml
1
2
3
4
5
6
7
|
< 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,否則就會崩潰。
1
2
3
4
|
//FrameLayout
rootLayout; CoordinatorLayout
rootLayout; //rootLayout
= (FrameLayout) findViewById(R.id.rootLayout); rootLayout
= (CoordinatorLayout) findViewById(R.id.rootLayout); |
結果:現在 FAB 隨着 Snackbar 的出現和消失而移動。還增加了一些功能。Snackbar 現在能夠滑動消失了!請試一試。
但是 bug 到處都是… bug 出現在低於 Android L 的系統上,當 Snackbar 滑動消失的時候,FAB 忘記了移動下來。
這顯然是一個 bug,但是我不知道確切的原因。感謝天主,這裏有一些解決方法。從我的實驗中,我發現當我們設置 FAB 的 margin bottom 和 margin right 爲一些非零的正數值時,它將會奇蹟般的正常工作,所以..就只需要爲低於 Android L 的系統改變 margin 的值爲 0.1dp就行。
res/values/dimens.xml
1
2
|
< dimen name = "codelab_fab_margin_right" >0.1dp</ dimen > < dimen name = "codelab_fab_margin_bottom" >0.1dp</ dimen > |
完成。下面是結果。
從現在起,如果你計劃使用 Android Design Support Library。請首先考慮 CoordinatorLayout,因爲它就像是這個 library 的核心。
步驟六:再見 ActionBar,你好,Toolbar
Toolbar 不是 Android Design Support Library 的一部分,而是在這個庫中需要與其他組件一起使用。
Toolbar 是一個替代傳統的 Action Bar 具有更靈活的行爲。我鼓勵你們從現在開始隱藏 Action Bar 並且切換到 Toolbar。因爲這些有奇妙功能的新庫,包括 Design Support Library 的組件中,都被設計爲和 Toolbar 協同工作而不是 Action Bar。
很容易切換到 Toolbar。只需要從 Activity 定義的 AppTheme 的 style 屬性隱藏掉 Action Bar 開始。
1
2
3
4
|
< style name = "AppTheme" parent = "Theme.AppCompat.Light.DarkActionBar" > < item name = "windowActionBar" >false</ item > < item name = "windowNoTitle" >true</ item > </ style > |
然後在 CoordinatorLayout 裏面的 FAB 之前正確的放一個 Toolbar 組件。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
< 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 > |
現在寫代碼來告訴系統,我們將使用 Toolbar 作爲一個 Action Bar,更換下面的 Java 代碼。
1
2
3
4
5
6
|
Toolbar
toolbar; private void
initInstances() { toolbar
= (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ... } |
雖然它現在可以運行成功,但是根據我之前說的,放在 CoordinatorLayout 的東西必須被設計和實現成與它一起合作的,否則將不與任何其他兄弟 views(sibling views) 協作。但是... Toolbar是不合適的。別擔心,這裏沒有任何新的特殊 Toolbar。只是一個組件是爲了準備讓 Toolbar 與 CoordinatorLayout 一起工作的更加完美。這是簡單的任務,只是簡單的用AppBarLayout包裹 Toolbar,就這樣!
1
2
3
4
5
6
7
8
9
10
11
12
|
< 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 > |
現在運行和測試,如果你做的都是對的,你將會看到 Drawer Menu 會覆蓋在 App Bar區域的頂部。使用了 AppBarLayout 的輸出結果是:低於應用欄區域的陰影現在回來了,耶!(譯者注:不曉得怎麼翻了:The outgrowth of applying AppBarLayout is the drop shadow below App Bar area is now returned ! Yah !)
這個步驟現在完成了。從現在開始,我建議你總是用 AppBarLayout 包裹 ToolBar 元素。光憑它能帶回來陰影的能力就足夠有說服力。
步驟7:在內容區域放東西
我們已經得到了 FAB 和 Toolbar,現在是時候在 Activity 的內容區域放上東西了。
額。如果是兩個簡單的按鈕呢?好吧,讓我們把它們放在在 AppBarLayout 和 FAB 之間。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
... </ 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 ...> |
下面是結果...
這些按鈕似乎都出人意料的放在了 Toolbar 下面。猜猜爲什麼?
是的,一些古老的原因,LinearLayout 沒有被設計成與 CoordinatorLayout 協同工作。在這樣的情況下,沒有任何佈局用來包裹 LinearLayout,使它像 Toolbar 的做法那樣。但它是更加容易的,你只需要在 LinearLayout 添加一個屬性告訴它的滾動行爲,就像下面寫的這樣:
1
2
3
4
|
< LinearLayout ... app:layout_behavior = "@string/appbar_scrolling_view_behavior" ...> |
現在,他們被放在了正確的位置了,耶!
完成!=)
步驟8:玩轉 TabLayout
Tab 是在 Android 應用程序中用戶體驗(UX)最佳實踐的一部分。在以前,如果我們想要使用新的材料設計風格的 Tab,我們需要自己去爲項目中下載 SlidingTabLayout 和 SlidingTabStrip 的源碼。現在,我們只需要使用這個庫提供的TabLayout,它也有很多可以調整的選項。
我們應該把 TabLayout 放在哪裏?根據 Android 應用程序用戶體驗指導原則,Tab 應該放在屏幕的頂部而不是在底部。還有,它應該在陰影部分的上面。所以,我們將其放在 AppBarLayout 裏面,沿着 Toolbar。這是可以做到的,因爲 AppBarLayout 是繼承自一個垂直的 LinearLayout。
1
2
3
4
5
6
7
|
< 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。
1
2
3
4
5
6
7
8
|
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" )); ... } |
下面是結果:
背景色會自動設置成 primary color(主題色),而導航線的顏色是強調色。但是你將會注意到 Tab 的字體仍然是黑色的,但是我們希望字體是白色的。這是因爲我們還沒有爲 TabLayout 提供任何主題呢。TabLayout 定義主題是簡單的,就像這樣:
1
2
3
|
< android.support.design.widget.TabLayout ... app:theme = "@style/ThemeOverlay.AppCompat.Dark.ActionBar" /> |
現在,他們是白色的了。
你可以像上面這樣選擇手動控制 TabLayout,或者讓它和 ViewPager 一起工作,自動調用setupWithViewPager(...)。我相信這種情況會很頻繁的使用。
還有,我們可以調整兩個屬性來顯示 TabLayout。
app:tabMode- 如果你想在屏幕上顯示出每個單獨的 tab,就設置 tab 爲fixed的, 。它適合只有少數 tab 的時候,但是如果有很多的 tab 的時候這是一個完全錯誤的選擇。在這種情況下你是不確定所有的 tab 是否能很好的在同一時間顯示出來的。所以,你可以設置這個屬性爲scrollable讓用戶去滾動 tab,就像 Google Play Store 那樣。
app:tabGravity- 如果你想要分配所有的可用空間給每個 tab,就設置這個屬性爲fill。如果你想要所有的 tab 在屏幕的中間,就設置這個屬性爲center。請注意,如果 tabMode 是設置成 scrollable 的,則這個屬性將會被忽略。
每個模式的樣子就像下面這樣:
TabLayout 完成了!
步驟9:當隨着內容滾動時,讓 AppBarLayout 退出屏幕
一個優美的 Android 用戶體驗是引導 App Bar 可以隨着內容滾動出屏幕的,以獲得額外的空間來顯示內容,並且,這已經是被證明這樣的用戶體驗是很棒的。以前有一些應用程序已經實現了這種行爲,但是開發者必須自己來實現。現在它只需要用一行代碼就能輕鬆的完成。
首先,我們需要讓內容能夠滾動,先往 LinearLayout 加入一些 Button。大約20個?
1
2
3
4
5
6
7
8
9
10
11
|
< 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 包裹這個 LinearLayout,還有,不要忘了將 LinearLayout 裏的 layout_behavior 移動到 ScrollView,因爲現在 ScrollView 是 CoordinatorLayout的最直接的子 view。
1
2
3
4
5
6
7
8
9
10
11
12
|
< 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 添加一個滾動標誌,就像這樣:
1
2
3
|
< android.support.v7.widget.Toolbar ... app:layout_scrollFlags = "scroll|enterAlways" /> |
試試吧
額... 原先假定的 Toolbar 會隨着內容的滾動滾出屏幕的,但是爲什麼它看起來什麼都沒有實現呢?
同樣的老原因啦... ScrollView 沒有被設計成與 CoordinatorLayout 協同工作(又來)。你需要另一個 view:NestedScrollView,Android Support Library v4 中有提供。這個 NestedScrollView 設計出來的目的就是爲了與 CoordinatorLayout 協同工作的。
1
2
3
4
5
|
< android.support.v4.widget.NestedScrollView ...> < LinearLayout ...> ... </ LinearLayout > </ android.support.v4.widget.NestedScrollView > |
同樣的原因,請注意了: ListView 類也是和 CoordinatorLayout 不能協同工作的。只有RecyclerView可以。也許需要時間來改變咯~
這裏將 ScrollView 改變成 NestedScrollView 後的結果。
運行起來真贊!你會注意到 Toolbar 滾出了屏幕,但是 TabLayout 仍然還在。這是因爲我們沒有給 TabLayout 設置任何滾動標誌。如果你想要 TabLayout 同樣從屏幕上消失,只需要給 TabLayout 定義相同的屬性就可以了。
1
2
3
|
< android.support.design.widget.TabLayout ... app:layout_scrollFlags = "scroll|enterAlways" /> |
結果:
這裏會有一些手勢上的 bug。我發現拉它回到屏幕是非常困難的。看來我們得等下一個版本了。
現在,讓我們來看看它的一些細節。很好奇這些標誌的真實意思是什麼:scroll和enterAlways?事實上我們可以在這裏設置4個屬性值。
scroll- 你想你想要設置這個 view 隨着內容滾動,你需要應用這個標誌。
enterAlwaysCollapsed- 這個標誌定義了 View 是如何回到屏幕的。當你的 view 已經聲明瞭一個最小高度(minHeight) 並且你使用了這個標誌,你的 View 只有在回到這個最小的高度的時候纔會展開,只有當 view 已經到達頂部之後它纔會重新展開全部高度。滾動標誌像這樣來使用它:scroll|enterAlwaysCollapsed。
它好像在這個 minHeight 部分死活不工作。這裏和 TabLayout 有另一個問題。很難把這些 View 拉回到屏幕來。
enterAlways- 這個標誌確保了任何向下滾動的操作都會讓這個 view 變得可見,達到“快速返回”(‘quick return’ )的效果,滾動標誌像這樣來使用它:scroll|enterAlways
exitUntilCollapsed- View 將關閉滾動直到它被摺疊起來(有 minHeight) 並且一直保持這樣,舉個例子:
1
2
3
4
5
6
7
|
< android.support.v7.widget.Toolbar ... android:layout_height = "192dp" android:gravity = "bottom" android:paddingBottom = "12dp" android:minHeight = "?attr/actionBarSize" app:layout_scrollFlags = "scroll|exitUntilCollapsed" /> |
下面是上述代碼的結果:
這種模式在組件中經常使用,我將在下一個部分討論。
步驟10: 移除 TabLayout
從實驗來看,在上述情況下當我們用 TabLayout 來滾動的時候,有一些明顯的 bug。我相信這只是一個 bug,而且以後會被修復的。現在,讓我們首先從代碼中移除 TabLayout,確保下一步運行是流暢的。
1
|
<!--android.support.design.widget.TabLayout
--> |
從 Java 代碼中也刪除
1
2
3
4
|
//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")); |
好了,讓我們去做下一步!
Step 11: Make Toolbar collapsable 步驟11:使工具欄可摺疊
就像在 exitUntilCollapsed 部分所示的例子中,Toolbar 可以展開和摺疊,但是你會看到它還不是很完美。Toolbar 仍然離開了屏幕,最好的體驗是讓這些 icon (漢堡等-即菜單欄) 應該留在屏幕內。
Design Support Library 已經爲這個準備好了。用CollapsingToolbarLayout你可以像魔術一樣讓 Toolbar 摺疊起來,就像其他組件一樣,它是非常容易使用的,具體操作步驟如下:
-
用CollapsingToolbarLayout包裹Toolbar,但仍然在AppBarLayout中
-
從Toolbar中刪除layout_scrollFlags
-
爲CollapsingToolbarLayout聲明layout_scrollFlags,並且將layout_scrollFlags設置成scroll|exitUntilCollapsed
- 改變 AppBarLayout 擴張狀態時的佈局高度大小。在這個例子中,我用 256dp
這是最終代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< 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 > |
這個結果是:
看起來不錯,但是這些 Toolbar icons 仍然滾出了屏幕。我們可以聲明這個屬性給 Toolbar 來固定住它,讓它總是在屏幕的頂部。
1
2
3
|
< android.support.v7.widget.Toolbar ... app:layout_collapseMode = "pin" /> |
Toolbar現在被定住了!
但是,等一下…標題的文字在哪裏?!不幸的是,在用 CollapsingToolbarLayout 包裹住 Toolbar 後,它隨風而逝了。我們必須通過在 Java 代碼中手動設置setTitle(String)來實現。
1
2
3
4
5
6
|
CollapsingToolbarLayout
collapsingToolbarLayout; private void
initInstances() { ... collapsingToolbarLayout
= (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout); collapsingToolbarLayout.setTitle( "Design
Library" ); } |
結果:
這裏的字體顏色仍然是黑的的。這是因爲我們還沒有爲 App Ba 設置任何主題。要做到這一點,只需要簡單的爲AppBarLayout聲明android:theme屬性就可以了,就像這樣:
1
2
3
|
< android.support.design.widget.AppBarLayout ... android:theme = "@style/ThemeOverlay.AppCompat.Dark.ActionBar" > |
現在,標題變成了白色的了!
由於CollapsingToolbarLayout 的 特點,應用的標題文字在收縮和展開狀態是會自動過渡的。如果你想要在展開狀態改變標題文字的位置,你可以這樣做:通過應用的 margin 的4個屬性,就是:app:expandedTitleMargin,app:expandedTitleMarginBottom,app:expandedTitleMarginEnd以及app:expandedTitleMarginStart
或者如果你想要在摺疊和展開狀態時改變文本的顯示。你可以這樣來簡單的實現:設置 TextAppearance,分別通過app:collapsedTitleTextAppearance和app:expandedTitleTextAppearance來設置。
讓我們從試着改變 margin 爲64dp 開始。
1
2
3
|
< android.support.design.widget.CollapsingToolbarLayout ... app:expandedTitleMarginStart = "64dp" > |
結果:
真棒!
步驟12:爲 App Bar 添加背景圖片
在這種情況下,我們想要用一張美麗的圖片作爲 App Bar 的背景,而不只是像現在這樣的一個普通的顏色。幸運的是 CollapsingToolbarLayout 是繼承自 FrameLayout 所以我們可以輕鬆的添加一個 ImageView 作爲 Toolbar 的背景圖層,就像這樣:
1
2
3
4
5
6
7
|
< ImageView android:layout_width = "match_parent" android:layout_height = "match_parent" android:scaleType = "centerCrop" android:src = "@drawable/header" /> <android.support.v7.widget.Toolbar ... |
結果:
圖片已經顯示出來了,但是這裏有一點還沒有達到預期,藍色的導航條仍舊顯示着。有一個 Toolbar 的背景看起來不是酷炫的。從 Toolbar 移除它,只需要下面這行代碼就行了。
1
|
android:background="?attr/colorPrimary" |
結果:
現在圖片是隨着內容的滾動了,但是看起來太呆了。我們可以使用視差模式讓它變得更優雅一些,只需要聲明 collapse 就行了,像下面這樣:
1
2
3
|
< ImageView ... app:layout_collapseMode = "parallax" /> |
結果:
你也可以設置視差的係數,介於 0.0-1.0之間。
1
|
app:layout_collapseParallaxMultiplier="0.7" |
請你自己去嘗試一下=)
最後你可能會注意到 App Bar 的背景總顯示一張圖片。你可以讓它在收縮的時候自動的變化到普通的顏色,通過聲明屬性 app:contentScrim 像下面這樣來實現:
1
2
3
|
< android.support.design.widget.CollapsingToolbarLayout ... app:contentScrim = "?attr/colorPrimary" > |
結果:
只用了幾行代碼,就讓 App Bar 變得這麼漂亮了 =)
步驟13:玩轉 Navigation Drawer
現在從左側拉出 Drawer Menu 仍然只是一個空白的面板。在以前,實現這個菜單是非常麻煩的,因爲我們不得不手動的用 LinearLayout 或者 ListView 去實現。
在 Android Design Support Library 中提供了 NavigationView,實現它變得更容易了,它爲我們節省了15.84321倍的時間!
首先,爲 Drawer Menu 創建一個標題視頻佈局文件。(它已經在 Github的項目中了)
res/layout/nav_header.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<? 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 > |
現在創建一個菜單資源文件
res/menu/navigation_drawer_items.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<? 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與兩個資源文件綁定起來,作爲 Drawer Menu 的菜單區域,用下面的代碼來替換一個已經存在的 白色的 LinearLayout :
1
2
3
4
5
6
7
8
9
10
11
12
|
... </ 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 > |
現在:召喚 Drawer Menu!哇喔,哇喔
NavigationView 就是爲了 Drawer Menu 而特別設計的。所以,所有的東西都會被創建並且自動測量包括菜單的寬度等,我們自己定義案例來配置以前的設計。
爲了處理這些菜單項的點擊事件,你可以聲明setNavigationItemSelectedListener來監聽,就像下面這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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 ; } }); } |
在實際使用中,請隨意的區聲明你想要定義的 header view 和修改菜單項。
步驟14:用上 TextInputLayout 讓 EditText 變的更風騷
這是 Codelab 的最後一部分了。你可以改變一箇舊的 EditText 的風格,讓它變得更時髦,即:總是會顯示一個提示或者一個錯誤信息。
要做到這一點,只需要簡單的用 TextInputLayout 包裹住一個 EditText ,就這麼簡單!
1
2
3
4
5
6
7
8
|
< 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 > |
把這兩個控件放到 NestedScrollView 裏看下結果。
難以置信的容易吧?=)
結論
Android Design Support Library 是非常有前途的支持庫,它非常值得在你的產品上使用。雖然它仍然包含了很多錯誤,我建議你再等等,直到每個錯誤都被修復。
這麼長的教程,希望希望你覺得它有用 =)
`
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!