用SnackBar替換掉Toast?看完再決定
一、前言
Design Support Library 是 Google 發布的一個全新的兼容函數庫,它可以在 Android 2.1 (Api level 7)及以上的設備中,實現 Material Design 的效果,這個函數庫同時也提供了一系列控件。
今天介紹的 SnackBar 就是其中之一。
在使用 Design Support Library 之前,需要在 build.gradle 文件中,添加依賴。
- compile 'com.android.support:design:25.3.0'
二、SnackBar 的常規使用
SnackBar 是一個輕量級的控件,它顯示在屏幕的底部,并且在顯示和隱藏的時候,帶有動畫效果。主要用于做一個快速的提示,它可以***的替代 Toast ,并且在使用的 API 上,也完全和 Toast 的使用方式類似,所以掌握起來,難度并不大。
它和 Toast ***的不同,是 SnackBar 可以帶有一個按鈕,也就是說它可以承載簡單的交互邏輯。
先來看看 SnackBar 能實現的效果。
可以看到,它會在底部顯示一個消息條,并且在顯示和隱藏的時候,都是自帶動畫的,可以承載一個TextView 和 Button。
SnackBar 的 API ,其實很多是參考了 Toast 的,所以他們的使用方式非常的相像。
就拿上面的例子中來看,代碼也非常的簡單。
SnackBar 之需要傳入一個 ViewGroup 然后它就可以在這個 ViewGroup 中顯示,通常我們會將這個 ViewGroup 置于屏幕的底部。
SnackBar 是沒有 public 的構造方法的,所以需要使用 make() 方法,獲取到一個 SnackBar 對象,然后調用 show() 方法,即可顯示出來。
在上面的例子中,還使用 setAction() 方法,為其右邊的按鈕設定文字以及一個點擊事件。
下面介紹一下 SnackBar 自帶的一些基本 api:
make():構造一個 SnackBar 對象,可進行簡單配置。
show():用于顯示一個已經構造好的 SnackBar。
setText():為 SnackBar 的設置提示的消息內容。
setAction():用于指定右邊的按鈕顯示的文字以及相應的惦記事件。
setActionTextColor():設定右邊按鈕文字的顏色。
setCallback():設置 SnackBar 的顯示和隱藏時候的回掉監聽。
setDuration():更新 Duration。
可以看到,SnackBar 本身只提供了非常簡單的 API 實現,看來 Google 是指望開發者完全按照他們的風格來設計 App。
三、帶著問題來看 SnackBar
前面已經介紹了 SnackBar 的基本 API 的使用,如果想做其他的設置,就需要我們自己進行一些操作了。那么接下來就讓我們帶著問題來看如何使用 SnackBar 。
下面會涉及到一些 SnackBar 的源碼,沒興趣的可以跳過直接看每個問題***的結論即可。
1、能不能設置一個常駐的 SnackBar
從上面的例子中可以看到,SnackBar 有點模仿 Toast 的意思,給出的兩個可供我們選擇的值,LENGTH_SHORT 、LENGTH_LONG ,分別表示兩個不同顯示時間的 SnackBar。
從代碼的文檔上看,貌似是沒有提供給我們用以設定常駐的 SnackBar 的方式。
但是細心看看源碼,可以發現,duration,是通過 @Duration 接口限定輸入的,而 duration 實際上是有三個取值的,另外一個就是可以設置常駐的。
所以,如果我們有對 SnackBar 有常駐需求的話,可以使用 LENGTH_INDEFINITE 標記即可。
2、去除掉滑動刪除功能
前面介紹過,SnackBar 是需要有一個 ViewGroup 容器來容納它的,而官方推薦使用 CoordinatorLayout 這個 ViewGroup,它實際上也是 Support Design Library 中提供的容器控件。
官方之所以推薦使用它,就是因為它可以讓用戶通過在 SnackBar 上進行右滑操作,進行刪除。
雖然說是這么說,我們還是從源碼中看看具體實現。
SnackBar 是繼承自 BaseTransientBottomBar 的,而這一段實現正是在父類中。
如圖所示,如果 SnackBar 的父布局是 CoordinatorLayout 的話,就使用 Behavior 來實現滑動刪除功能。
所以我們如果不需要滑動刪除的功能,可以考慮用一個 FrameLayout 來容納 SnackBar。
或者需要滑動功能,卻發現沒有實現,檢查一下布局,看承載 SnackBar 的容器,是不是 CoordinatorLayout。
3、禁用動畫能做到嗎?
不知道會不會有一些交互設計師要求不要動畫,就這么生硬的顯示出來。那么我們來看看到底動畫是不是可以被禁用掉。
執行 SnackBar 顯示和隱藏動畫的邏輯,依然在它的父類BaseTransientBottomBar 中。查看源碼可以看到,它在執行顯示和隱藏之前,都會調用 shouldAnimate() 方法,來判斷是否需要執行一個動畫。
這么看,好像 SnackBar 是可以支持關閉動畫的,再看看 animateView 的實現。
是否使用動畫是依賴 AccessibilityManager 中的 enable 屬性決定的,而它是一個私有的屬性,并且沒有提供修改它的方法,并且如果用反射修改它的值,不確定會不會出現其他的問題,有待驗證。
那么可以簡單的認為,SnackBar 的動畫,是無法簡單關閉的。
4、讓 SnackBar 顯示在頂部可以嗎?
既然 SnackBar 是有一個外部容器來承載它的,也就是說,容器在哪里,它實際上就出現在哪里。
所以如果將它置為頂部,其實是可以讓它在頂部出現的。但是你以為這樣就完了嗎?還需要考慮動畫的問題,雖然 SnackBar 會出現在頂部,但是動畫依然是從下到上出現的,你就會得到一個非常詭異的 SnackBar 。
這明顯不是我們想要的。那么是不是想辦法改變它出現和隱藏的動畫就可以了,繼續在源碼內找答案。
animateViewIn() 方法就是 SnackBar 顯示時候調用的動畫,但是實際上,它無法被重寫。
所以,將 SnackBar 置于頂部,并且***的執行動畫的設想是達不到的。
5、修改其他的UI樣式可以實現嗎?
SnackBar 原本提供的可以修改 UI 樣式的 API 非常的少,它只能修改右邊 Button 字體的顏色。
我們繼續在源碼內找答案,看看源碼可以發現,它的布局是在 SnackBar 中 inflater 出來的。布局文件為,design_layout_snackbar_include.xml
SnackBar 就是用一個 TextView 和一個 Button 實現的。也就是說,我們可以直接找到這兩個控件,來改變它的樣式。
參考 setText() 方法可以看到,實際上它是通過 mView 對象,拿到一個 SnackbarContentLayout 對象進行操作。mView 這個 View 就是我們需要的。SnackBar 正好也提供了它的 get 方法,所以只需要拿到它,然后對其內的 View 進行樣式的修改,即可達到我們的需求。
所以,對于 SnackBar 的樣式修改,只要通過 getView() 拿到 mView 對象之后,就可以實現樣式的修改了。
四、題外話再說兩句
帶著問題看源碼是一個非常好的讀源碼的方式。實際上 SnackBar 用起來,看上去非常的好用,但是它封裝的東西太多了。如果我們親愛的設計師能遵照 Material Design 來設計 App,其實直接用 SnackBar 也是一個不錯的選擇。
對于一些定制要求的類似 SnackBar 的實現。實際上我們已經把 SnackBar 的源碼讀了一遍了,關鍵點已經掌握,自己參照 SnackBar 的源碼實現一套我們自己的 XxSnackBar 也并不難,都是自己寫的代碼了,如何實現就看我們自己的了。
我帶著的問題,實際上也是我看到 SnackBar 會想到的問題。
【本文為51CTO專欄作者“張旸”的原創稿件,轉載請通過微信公眾號聯系作者獲取授權】









































