本文旨在分享本身在 ToolBar 使用上的偷懶,沒有較多代碼,只是分享一種思路。git
這裏指的 ToolBar 是泛指頂部的那個功能區域,不只僅侷限於 Android 中的 ActionBar、ToolBar 。github
ToolBar 應該算是在項目中使用較爲普遍的一個 View 了,它主要用於展現當前頁面的標題、導航按鈕及可能存在的擴展功能。app
下圖就展現了一個具備一些基本屬性的 ToolBar。佈局
在 Android 中也不難實現,無非就是在佈局文件聲明出 ToolBar,而後在 Activity 使用它,爲其設置一些內容,若是須要用到右側的菜單,爲其添加菜單便可。post
不過呢,國內的應用大多沒有按照這種樣式去設計,咱們公司也是如此。這裏咱們不去評判它該是怎麼樣的,只是來討論如何快速作出與設計師要求相同的效果。spa
QQ 的 ToolBar設計
小米短信的 ToolBar3d
我司產品的 ToolBarcode
遇到這種狀況,無非就是經過自定義 View,作出一個相似 ToolBar 的 View,而後放在頁面的最靠近狀態欄的地方。而後每一個頁面經過在佈局文件中進行聲明,再到相應的 Activity 或是 Fragment 中經過 findViewById 的形式找到再設置一些相關屬性之類的。cdn
難嗎?不難,就是有點噁心,每一個頁面都得寫一些重複代碼,我是真的不想寫啊。那就一塊兒來偷個懶吧。
我不想在每一個 Activity 或是 Fragment 上都寫那些噁心的代碼,能不能少些一點?
通過觀察我發現,大部分頁面的 ToolBar 都是中間是標題,左側是返回按鈕,右側可能爲文字、圖片或是什麼都不顯示。區別最大的就是底部的內容,那麼我把底部的內容所有劃分給 Fragment、Toolbar 所屬的區域屬於承載 Fragment 的 Activity。(以下圖所示)
這樣一來,開發者只須要在 Activity 中寫,在 Activity 加載不一樣的 Fragment 時由 Fragment 去更新當前的標題。當用戶點擊了左側或右側的功能按鍵,經過查找當前 Activity 中存在的 Fragment 列表,找出當前顯示的 Fragment,把點擊事件傳入便可。
這樣一來,既能實現了 UI 效果也少些了部分代碼,挺好的。到這還沒完,來思考一下這種方案的優缺點。
優勢是不須要在每一個 Fragment 中聲明並設置 ToolBar 了,在必定程度上減小了 ToolBar 聲明與設置屬性的次數。
不過呢這種方案的缺點也很明顯,因爲把 ToolBar 放在了 Activity,因此每次設置 ToolBar 的相關屬性都必須通過 Activity,Activity 要保證其承載的每一個 Fragment 顯示不出問題,就必須作到對每一個 Fragment 的狀況作到兼容。好比,有個頁面須要放置一張圓弧的背景圖,此時這種方案作起來就會比較麻煩。
因此,總體來講,只是把粗略的把重複代碼的解決掉,可是又會帶來某些兼容的問題。也不算多好,因此才又有了新的偷懶版本。
自從 Android Studio 大力推行 ConstraintLayout 以後,我就一直在使用,發現了能夠利用 ConstraintLayout 動態設置 View 的約束從而達到設置 ToolBar 的效果。
具體來講是這樣的:
首先使用 Java 代碼動態建立出 ToolBar 這個 View。緊接着爲 ToolBar 設置約束條件,分別爲:
這樣一來,就能讓 ToolBar 位於整個頁面的頂端位置。
不過這樣有一個致命的痛,就是會覆蓋原有屬於頂部的 View(closeToolView),解決這個問題也很簡單,利用 ConstraintLayout 爲頂端 View (closeToolView)設置一個 top_toBottomOf 這個屬性,這個屬性值固然就是 ToolBar 了。
怎麼樣,這個方案是否是挺簡單的,不過,稍微想想就會發現,咱們在佈局文件裏不就這麼寫的嗎?有什麼稀奇的。
這個方案沒什麼稀奇的,只是把本來屬於開發者寫在佈局文件中的內容,放在了 Java 代碼中而已。
不過稍稍變通一下,咱們在頁面的佈局文件也就不須要寫這麼多的重複代碼了,並且,能夠建立出一個 ToolBarHelper 這個類,由這個類去完成上述的這些內容,如此一來,Activity 與 Fragment 的基類只須要去調用 ToolBarHelper 便可。
這樣這個流程下來就算是完事了,不過你要是想用的話仍是得清楚他的不足之處:
根佈局必須爲 ConstraintLayout
這個很好理解,若是不是 ConstraintLayout 這個佈局的話,後續全部的約束條件都不能使用;
頂部 View (closeToolView)的高度不能設置爲 match_parent
若是頂部 View(closeToolView)的高度爲 match_parent 的話,那麼即便設置了 top_toBottomOf 頂部 View(closeToolView)的相對位置也不會發生改變。
關鍵的代碼以下:
private fun addToolBar(root: View, closeToolView: View) {
val layoutParams = ConstraintLayout.LayoutParams(root.layoutParams)
layoutParams.width = ConstraintLayout.LayoutParams.MATCH_PARENT
layoutParams.height = 48.toPix()
toolView.layoutParams = layoutParams
toolView.id = View.generateViewId()
if (initTitle.isNotEmpty()) {
toolView.setTitle(initTitle)
}
if (root is ConstraintLayout) {
val relation = ConstraintSet()
checkId(root)
root.addView(toolView)
relation.clone(root)
relation.connect(toolView.id, ConstraintSet.START, root.id, ConstraintSet.START)
relation.connect(toolView.id, ConstraintSet.END, root.id, ConstraintSet.END)
relation.connect(toolView.id, ConstraintSet.TOP, root.id, ConstraintSet.TOP)
relation.applyTo(root)
val closeRelation = ConstraintSet()
closeRelation.clone(root)
closeRelation.connect(
closeToolView.id,
ConstraintSet.TOP,
toolView.id,
ConstraintSet.BOTTOM
)
closeRelation.applyTo(root)
}
}
複製代碼
筆者作了一個簡單的 Demo,並上傳到了 GitHub,這個 Demo 僅用於演示 ToolBarHelper 的使用,後續可能包含 ToolBar 的點擊相應及多樣化的設置,沒有什麼特別的功能,其中 Fragment 的導航庫使用的是 AndroidX 的 Navigation。
有什麼問題或是狀況,能夠在評論或是 issues 中留言。😉
封面圖:Photo by Amber Walker on Unsplash
推薦閱讀:來學一波 Navigation