Android Design Support Library--簡約而不簡單的SnackBar

引言

在之前我有提到這一篇Android Design Support Library系列文章是關于SnackBar的,但是由于要用到CoordinatorLayout所以先翻譯了一篇相關文章,如果還不了解的可以先看一下Android Design Support Library--使用CoordinatorLayout來處理滾動 ,這一篇我們講SnackBar,SnackBar其實就是Toast的升級版,他們之間最大的不同就是:SnackBar會對我們的操作提供一個輕量級的反饋,并且可以對點擊事件做出響應,如果是在手機上使用一個SnackBar的話,我們會看到在屏幕底部出現一條簡短的信息,如果是在更大的屏幕上這條信息應該會顯示在左下角,并且當一個SnackBar顯示的時候它是凌駕于當前所有屏幕元素之上的,我們在屏幕上一次只能顯示一個SnackBar,如果這么講不是很清楚的話,我們先來看一個小Demo,通過代碼驅動理解是比較好的方式。

示例

根據SnackBar的特點,在屏幕上顯示出不同的SnackBar,效果如下:
這里寫圖片描述

先看一下相關的API文檔:

方法類型 方法 作用
void dismiss() 使SnackBar消失
int getDuration() 返回SnackBar的持續時間
View getView() 返回當前SnackBar的View
boolean isShown() 判斷該SnackBar是否正在顯示
boolean isShownOrQueued() 判斷該SnackBar是否正在顯示或者排隊等待即將要顯示
static Snackbar make(View view, int resId, int duration) 新建一個用來顯示信息的SnackBar
static Snackbar make(View view, CharSequence text, int duration) 同上
Snackbar setAction(int resId, View.OnClickListener listener) 設置這個即將顯示的SnackBar的動作
Snackbar setAction(CharSequence text, View.OnClickListener listener) 同上
Snackbar setActionTextColor(ColorStateList colors) 設置action的文字顏色(右邊的)
Snackbar setActionTextColor(int color) 同上
Snackbar setCallback(Snackbar.Callback callback) 設置一個回調,當SnackBar的可見性改變的時候調用
Snackbar setDuration(int duration) 設置SnackBar信息的顯示時間
Snackbar setText(int resId) 更新SnackBar上顯示的文字
Snackbar setText(CharSequence message) 同上
void show() 顯示SnackBar,最后一定要調用這個方法,不然SnackBar不顯示,聯想Toast

可以看到Demo上顯示了三種不同的SnackBar,我們都知道SnackBar是Toast的升級版,但也說明了一個問題那就是SnackBar是用來顯示消息的,同時根據你的需求不同可以對這些消息做出一定的響應動作,下面分析三種顯示消息方式的不同: - 普通的SnackBar也許有的人并沒有過多的需求,只是單純地想把SnackBar當作一個顯示消息的控件而已,那么可以很簡單的在代碼中這么使用:

Snackbar.make(mCoor, R.string.normal, Snackbar.LENGTH_SHORT).show();```

對比一下我們的Toast方式:

Toast.makeText(MainActivity.this,R.string.normal,Toast.LENGTH_SHORT).show();

是不是很像,沒錯簡單的使用的話SnackBar跟Toast并沒有多大區別,但是動畫效果上是有差異的,如果你注意到了這一點:![這里寫圖片描述](http://upload-images.jianshu.io/upload_images/735909-4b19f74a32c3fe35?imageMogr2/auto-orient/strip)
看,這個側邊滑動消失的效果只有當你使用CoordinatorLayout作為根布局才有,這就是為什么在寫SnackBar之前我要先說明一下CoordinatorLayout的原因,如果你使用普通的LinearLayout或者RelativeLayout是不會有這種動畫交互效果的,另外,**注意**SnackBar的make方法有兩種重載方法,分別是:

make(View view, int resId, int duration)

make(View view, CharSequence text, int duration)

這里有三個參數,第一個參數View表示的意思是我們傳入一個View,然后SnackBar會遍歷整個View Tree來找到一個合適的View承載SnackBar的View,如果你想要實現上面的動畫交互效果的話最好是傳入CoordinatorLayout對象,第二個參數的話是兩個重載方法不同的地方,有一種是我們熟知的:

Snackbar.make(mCoor, "普通的SnackBar", Snackbar.LENGTH_SHORT).show();

還有一種要求傳入一個ID,注意這個ID并不是指其他的什么,就是你在string.xml文件中定義的字符串資源的ID,比如這樣:

Snackbar.make(mCoor, R.string.normal, Snackbar.LENGTH_SHORT).show();

然后第三個參數是SnackBar的持續時間,只有三種:

1、Snackbar.LENGTH_INDEFINITE 一直顯示直到另一個SnackBar出現或者主動調用了dismiss()方法
2、Snackbar.LENGTH_SHORT 顯示較短的時間
3、Snackbar.LENGTH_LONG 顯示較長的時間

但是官方文檔是這么描述的:

either be one of the predefined lengths: LENGTH_SHORT, LENGTH_LONG, or a custom duration in milliseconds.

說是可以自定義顯示時間,但是我自己試了確實不可以,應該是API文檔的一個小bug,如果誰試成功了趕緊告訴我~~如果使用過Toast的話上面的應該很好理解,好了,如果你的業務中對SnackBar并沒有更多的要求,那么最普通的SnackBar應該滿足了,接下來看稍微高級一點的:
 - 帶回調的SnackBar:如果還不太清楚回調的話可以看看這個**[Android回調函數機制那點事](http://blog.csdn.net/wei_smile/article/details/51040034)** ,講這個之前先提一點,如果我們想更加靈活的使用Snackbar的話最好是先持有它的引用,也就是:

private Snackbar mSnackBar

原因很簡單,你會發現上面提供的常用API中很多方法都是非靜態方法并不是靜態方法,你要調用的話只能通過SnackBar對象去調用。然后說SnackBar回調之前先說一下Action,SnackBar提供了一個setAction方法:

1、setAction(int resId, View.OnClickListener listener)
2、setAction(CharSequence text, View.OnClickListener listener)

同樣是兩個重載方法,第一個參數跟前面解釋的一樣,第二個參數是我們熟知的對點擊事件的監聽,使用方法如下:

Snackbar.make(mCoor,R.string.callback,Snackbar.LENGTH_SHORT)
.setAction(R.string.UNDO, new View.OnClickListener() {
@Override public void onClick(View v) {
// do something }
}).show();

看一下效果:![這里寫圖片描述](http://upload-images.jianshu.io/upload_images/735909-fa34bdd06067d30f?imageMogr2/auto-orient/strip)當我們調用了setAction方法并且傳入一個字符串之后,SnackBar的右下角就會呈現出我們傳入的字符串,并且這個字符串是可點擊的,我們可以在點擊事件里面做出響應,比如說跳轉Activity或者彈出一個Toast等等,這里默認你點擊了這個Action這個SnackBar是會消失的。也就是無論你的duration參數設置的是一直顯示還是顯示多長時間都會消失。有些人可能對右下角這個文字的顏色不滿足想要改變,沒問題,你想到的Google都給你想好了,SnackBar專門提供了方法來更改Action的文字顏色:

1、setActionTextColor(ColorStateList colors)
2、setActionTextColor(int color)

這里第一種方式不建議用,太復雜,你要想這么用也行:

Resources resource = (Resources) getBaseContext().getResources();
ColorStateList csl = (ColorStateList)
resource.getColorStateList(R.color.PeachPuff);
mSnackBar.setActionTextColor(csl);

這是網上找到的一種方式,但是我還是推薦使用第二種方式來更改Action的文字顏色,可以看到是我們熟悉的傳入一個int型的值,我提供如下幾種方式更改:

1、mSnackBar.setActionTextColor(Color.rgb(232,44,123))
2、mSnackBar.setActionTextColor(Color.BLUE)
3、mSnackBar.setActionTextColor(Color.parseColor("#FFDAB9"));

對了,我還發現一種額外的方式,我們現在使用Android Studio創建新的Project時候系統都會默認在style.xml文件夾下面生成這個:

<style name="AppTheme" parent="The
e.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> </style>

這里的```<item name="colorAccent">@color/colorAccent</item>```其實也可以更改Action文字顏色,而且默認的Action文字顏色就是這里設置的顏色,但是有一個缺點就是如果你改動了這里,那么很多Material Design控件的相關顏色都會改變,如果你看過我之前寫的[**Android Design Support Library--TextInputLayout的使用**](http://blog.csdn.net/wei_smile/article/details/51284964)你會知道TextInputLayout下劃線的顏色也是通過這個屬性來更改的,所以為了穩定起見還是使用官方提供的方法去更改吧,我這純屬抖個機靈。
那么回到正題,講講SnackBar的回調,眼尖的朋友可能發現了,我的Demo里面帶回調的SnackBar在彈出和消失的時候都會有Toast通知出現,其實就是使用了SnackBar自帶的

setCallback(Snackbar.Callback callback)

方法,這里需要傳入一個`Snackbar.Callback callback` 參數,其實這個,這個Callback 是SnackBar內部的一個抽象類,它內部有兩個空實現的方法:

onDismissed(Snackbar snackbar, int event)
onShown(Snackbar snackbar)

顧名思義,我們可以可以分別在這兩個方法中定義出當SnackBar消失和產生時我們需要做的事,這兩個方法會在SnackBar消失和產生時被回調,打個比方:

mSnackBar.setCallback(new Snackbar.Callback() { @Override public
void onDismissed(Snackbar snackbar, int event) {
super.onDismissed(snackbar, event);
Toast.makeText(MainActivity.this,"SnackBar
Dismiss",Toast.LENGTH_SHORT).show(); } @Override public void
onShown(Snackbar snackbar) { super.onShown(snackbar);
Toast.makeText(MainActivity.this,"SnackBar
Show",Toast.LENGTH_SHORT).show(); } });

這樣就實現了在SnackBar消失和產生時彈出Toast通知的動作,其他具體的邏輯可以自己去實現。
####完全自定義你自己的SnackBar
如果你對上述使用還是不甚滿意,那么接下來我教你怎么自定義你自己的SnackBar,說實話用到的場景并不多,但是學了就學個透徹,這一部分知識的靈感來自于[**沒時間解釋了,快使用Snackbar!**](http://www.lxweimin.com/p/cd1e80e64311) ,SnackBar并沒有提供更改背景或者其他樣式的方法,但是我們可以通過查看源碼來試試可不可以自定義自己樣式,我們找到這么一段代碼:

private Snackbar(ViewGroup parent) { mTargetParent = parent;
mContext = parent.getContext();
ThemeUtils.checkAppCompatTheme(mContext); LayoutInflater
inflater = LayoutInflater.from(mContext); mView = (SnackbarLayout)
inflater.inflate( R.layout.design_layout_snackbar, mTargetParent,
false);
}

最后一行的inflate是不是很熟悉,我們可不可以認為Snackbar的布局就是這么加載的,這個SnackBarLayout是在SnackBar內部定義的一個繼承自LinearLayout的內部類:

public static class SnackbarLayout extends LinearLayout { private
TextView mMessageView; private Button mActionView; private int
mMaxWidth; private int mMaxInlineActionWidth;

看到這幾個變量的定義,我已經確定了上面的想法,接下來我們找到上面代碼加載的那段布局:

<merge
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:id="@+id/snackbar_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_weight="1"
android:paddingTop="14dp" android:paddingBottom="14dp"
android:paddingLeft="12dp" android:paddingRight="12dp"
android:textAppearance="@style/TextAppearance.Design.Snackbar.
Message" android:maxLines="2"
android:layout_gravity="center_vertical|left|start"
android:ellipsize="end" android:textAlignment="viewStart"/>
<Button android:id="@+id/snackbar_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp" android:layout_marginStart="0dp"
android:layout_gravity="center_vertical|right|end"
android:paddingTop="14dp" android:paddingBottom="14dp"
android:paddingLeft="12dp" android:paddingRight="12dp"
android:visibility="gone" android:textColor="?attr/colorAccent"
style="?attr/borderlessButtonStyle"/></merge>

看到這兩個控件的ID了么

android:id="@+id/snackbar_text"
android:id="@+id/snackbar_action"

public View getView ()Returns the Snackbar's view.

這個方法可以返回我們SnackBar的View,那么這個View是什么,看源碼:
那么第一個就是SnackBar左邊顯示的message,第二個就是我們設置了action時候顯示的Button咯,這就簡單了,如果你仔細看了上面提供的API文檔你會發現有這么一個方法:

/** * Returns the {@link Snackbar}'s view. */
@NonNull public View getView() { return mView; }

找一下mView在哪里定義的:

private final SnackbarLayout mView;mView = (SnackbarLayout)
inflater.inflate( R.layout.design_layout_snackbar, mTargetParent,
false);

好了,這下一切都清楚了,接下里示范一下怎么自定義你自己的SnackBar:

private View view; ....省略中間代碼 view =
mCustomSnackBar.getView();
if (view != null) {
view.setBackgroundColor(Color.parseColor("#7B68EE"));
//獲取Snackbar的message控件,修改字體顏色
((TextView)view.findViewById(R.id.snackbar_text)).setTextColor(Color.parseColor("#FFDAB9"));
//添加圖標
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) view;
//添加自定義布局,這里布局就包含了一個ImageView
//custom_layout是你自定義的布局
View add_view = LayoutInflater.from(view.getContext()).inflate(R.layout.custom_layout
, null);
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONT
ENT, LinearLayout.LayoutParams.WRAP_CONTENT);
p.gravity = Gravity.CENTER_VERTICAL; //數字表示新加的布局在SnackBar中的位
置,從0開始,取決于你SnackBar里面有多少個子View
snackbarLayout.addView(add_view, 0, p); }

**最后一行,addView方法第二個參數表示新加的布局在SnackBar中的位置,注意不要超過總的View的個數不然會報錯**,message和Action text分別算一個View,其他的話注釋已經寫得很清楚就不一一解釋了,這個代碼呈現的效果如下:![這里寫圖片描述](http://upload-images.jianshu.io/upload_images/735909-67af16b27639f7e7?imageMogr2/auto-orient/strip)

[項目源碼](http://download.csdn.net/detail/wei_smile/9512799)
[項目GitHub地址](https://github.com/GiitSmile/SnackBarDemo)
另外,最近做了一個小Demo,主要是使用了[**ExplosionField**](https://github.com/AndroidPath/ExplosionField) 的View爆炸效果,效果圖如下:![這里寫圖片描述](http://upload-images.jianshu.io/upload_images/735909-7ad99335a02ba20f?imageMogr2/auto-orient/strip)![這里寫圖片描述](http://upload-images.jianshu.io/upload_images/735909-90436a7d8a3fa7d8?imageMogr2/auto-orient/strip)有興趣的可以看看:[GitHub地址](https://github.com/GiitSmile/Campus)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容