Android-SnackBar

SnackBar是DesignSupportLibrary中的一個重要的控件,用於在界面下面提示一些關鍵信息,跟Toast不同的地方是SnackBar允許用戶向右滑動消除它,同時,也允許在SnackBar中設定一個Action,當用戶點擊了SnackBar裏面的按鈕的時候,可以進行一些操作,所以,功能絕對是很強大的。

SnackBar的構造:

// 參數分別是父容器,提示信息,持續時間
public
static Snackbar make(@NonNull View view, @NonNull CharSequence text,@Duration int duration)

SnackBar的常用方法:

// 用於給SnackBar設定一個Action,點擊之後會回調OnclickListener中的Onclick方法
public
Snackbar setAction(CharSequence text, final View.OnClickListener listener)
// 用於設定Action的字體顏色
public
Snackbar setActionTextColor(@ColorInt int color)
// 設定提示的字體
public
Snackbar setText(@NonNull CharSequence message)
// 展示SnackBar
public
void show()
// 清除SnackBar
public
void dismiss()
// 設置回調,比如OnDismissed或者OnShown
public
Snackbar setCallback(Callback callback)

簡單是使用就不說了,這裏直接說一下設置Action和設置Callback

要求:實現一個撤銷的操作,用戶點擊列表中每一項的刪除按鈕之後,刪除(數據庫中)對應的行,然後彈出SnackBar提示是否撤銷,如果選擇是,則恢復刪除的行,否則刪除該行,效果如下

思路:

①先對我們要刪除的這個行進行備份,然後在點擊刪除按鈕的時候把行刪除,把數據從源刪除,然後刷新

②彈出SnackBar,設定Action,如果點了Yes,就把數據插入到源中,刷新

③設定CallBack,監聽SnackBar的消失事件,如果不是點擊Action而消失,則將數據從SQLite中刪除

注意:這裏爲什麼要設定監聽?因爲如果不進行監聽,需要在刪除的時候訪問數據庫,撤銷是時候進行數據庫的插入操作,這樣列表中的條目位置會發生改變

我們可以在Adapter中對刪除按鈕進行監聽,代碼如下:

複製代碼
 1 delete.setOnClickListener(new View.OnClickListener() {
 2             @Override
 3             public void onClick(View v) {
 4                 backupMap = mdata.get(position);
 5                 mdata.remove(position);
 6                 notifyDataSetChanged();
 7                 Snackbar snackBar = Snackbar.make(root, "是否撤銷刪除?", Snackbar.LENGTH_LONG);
 8                 snackBar.setAction("YES", new MyOnClickListener(position));
 9                 snackBar.setCallback(new MyCallback());
10                 snackBar.show();
11             }
12         });
複製代碼

如果點擊了刪除按鈕,我們對數據進行備份,然後刪除數據源mdata對應的這個map,接下來刷新界面。然後第7行構造了一個SnackBar,詢問是否撤銷刪除,並把持續時間設置爲LONG以免用戶未閱讀完文字SnackBar就消失了。第8行設定了點擊的Action,提示信息爲“YES”,並提供了點擊的監聽。第9行設定了Callback,判斷SnackBar的消失狀態。第10行就直接把SnackBar展示出來。

下面來看Action的點擊事件:

複製代碼
 1 private class MyOnClickListener implements View.OnClickListener {
 2         private final int position;
 3 
 4         public MyOnClickListener(int position) {
 5             this.position = position;
 6         }
 7 
 8         @Override
 9         public void onClick(View v) {
10             mdata.add(position, backupMap);
11             notifyDataSetChanged();
12         }
13     }
複製代碼

如果點擊了Action,就用備份的數據進行恢復

下面看Callback:

複製代碼
 1 private class MyCallback extends Snackbar.Callback {
 2         @Override
 3         public void onDismissed(Snackbar snackbar, int event) {
 4             super.onDismissed(snackbar, event);
 5             if (event == DISMISS_EVENT_SWIPE || event == DISMISS_EVENT_TIMEOUT || event ==
 6                     DISMISS_EVENT_CONSECUTIVE) {
 7                 mdatabase.execSQL("delete from tally where id=?", new String[]{backupMap.get
 8                         ("id").toString()});
 9             }
10         }
11     }
複製代碼

這裏看到我們重寫了onDismissed方法,在SnackBar消失的時候會回調這個方法,我們先判斷這個消失的類型,如果是點擊了Action,就不用刪除數據庫中的數據,否則就對數據庫進行刪除。

SnackBar消失的類型對應的常量:

複製代碼
/** Indicates that the Snackbar was dismissed via a swipe.*/
public static final int DISMISS_EVENT_SWIPE = 0;
/** Indicates that the Snackbar was dismissed via an action click.*/
public static final int DISMISS_EVENT_ACTION = 1;
/** Indicates that the Snackbar was dismissed via a timeout.*/
public static final int DISMISS_EVENT_TIMEOUT = 2;
/** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
public static final int DISMISS_EVENT_MANUAL = 3;
/** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
public static final int DISMISS_EVENT_CONSECUTIVE = 4;
複製代碼

可以看到,從0到4分別是滑動清除、點擊Action、持續時間結束、調用dismiss方法以及有新的SnackBar產生

因爲每次SnackBar消失的時候都是調用了dismiss方法,所以我們只需要判斷此時的狀態是否爲滑動清除、持續時間結束或者新的SnackBar產生,如果是,則證明用戶不需要撤銷,則對數據庫進行刪除。

最後,如果使用SnackBar,記得添加依賴!!


一、前言

Design Support Library 是 Google 發佈的一個全新的兼容函數庫,它可以在 Android 2.1 (Api level 7)及以上的設備中,實現 Material Design 的效果,這個函數庫同時也提供了一系列控件。

今天介紹的 SnackBar 就是其中之一。

在使用 Design Support Library 之前,需要在 build.gradle 文件中,添加依賴。

  1. 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 對象之後,就可以實現樣式的修改了。



發佈了30 篇原創文章 · 獲贊 34 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章