引言
在之前我有提到這一篇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并沒有多大區別,但是動畫效果上是有差異的,如果你注意到了這一點:
看,這個側邊滑動消失的效果只有當你使用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();
看一下效果:當我們調用了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://download.csdn.net/detail/wei_smile/9512799)
[項目GitHub地址](https://github.com/GiitSmile/SnackBarDemo)
另外,最近做了一個小Demo,主要是使用了[**ExplosionField**](https://github.com/AndroidPath/ExplosionField) 的View爆炸效果,效果圖如下:有興趣的可以看看:[GitHub地址](https://github.com/GiitSmile/Campus)