Snackbar在Android中的使用日益廣泛,很大程度上替代了傳統的Toast,相比Toast擁有更好的使用體驗.
但在我們的APP中,很多時候用到Snackbar,都需要對Snackbar的樣式及顯示效果進行一些定制以滿足不同的使用場景,比如設置文字顏色,背景顏色,自定義顯示時間,設置Snackbar顯示的具體位置,設置文字的對齊方式,甚至是向Snackbar布局中添加View等,為了實現這些需求,將一些方法整理出來,應該可以節約開發者部分的時間.
標題中的說的'一行代碼'在某些使用場景下有些夸大其詞,實際意思是指SnackbarUtils中的方法支持鏈式調用,可以快捷的為Snackbar設置多種效果!
源碼及所在DEMO已上傳至GitHub:SnackbarUtils,歡迎大家提Bug,或者有關于Snackbar新的使用場景也可以交流,我會繼續把這個工具類繼續完善!
廢話不多說,直接上效果圖:
已經實現的功能點:
1:設置Snackbar顯示時間長短
2:設置Snackbar背景顏色
3:設置TextView(@+id/snackbar_text)的文字顏色
4:設置Button(@+id/snackbar_action)的文字顏色
5:設置Snackbar背景的透明度
6:設置Snackbar顯示的位置
7:設置Button(@+id/snackbar_action)文字內容 及 點擊監聽
8:設置Snackbar展示完成 及 隱藏完成 的監聽
9:設置TextView(@+id/snackbar_text)左右兩側的圖片
10:設置TextView(@+id/snackbar_text)中文字的對齊方式
11:向Snackbar布局中添加View(Google不建議,復雜的布局應該使用DialogFragment進行展示)
12:設置Snackbar布局的外邊距
13:設置Snackbar布局的圓角半徑值
14:設置Snackbar布局的圓角半徑值及邊框顏色及邊框寬度
15:設置Snackbar顯示在指定View的上方
16:設置Snackbar顯示在指定View的下方
SnackbarUtils代碼:
import android.annotation.TargetApi;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.Space;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
public class SnackbarUtils {
//設置Snackbar背景顏色
private static final int color_info = 0XFF2094F3;
private static final int color_confirm = 0XFF4CB04E;
private static final int color_warning = 0XFFFEC005;
private static final int color_danger = 0XFFF44336;
//工具類當前持有的Snackbar實例
private static Snackbar mSnackbar = null;
private SnackbarUtils(){
throw new RuntimeException("禁止無參創建實例");
}
public SnackbarUtils(@NonNull Snackbar snackbar){
this.mSnackbar = snackbar;
}
/**
* 獲取 mSnackbar
* @return
*/
public Snackbar getSnackbar() {
return mSnackbar;
}
/**
* 初始化Snackbar實例
* 展示時間:Snackbar.LENGTH_SHORT
* @param view
* @param message
* @return
*/
public static SnackbarUtils Short(View view, String message){
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_SHORT);
return new SnackbarUtils(mSnackbar).backColor(0XFF323232);
}
/**
* 初始化Snackbar實例
* 展示時間:Snackbar.LENGTH_LONG
* @param view
* @param message
* @return
*/
public static SnackbarUtils Long(View view, String message){
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_LONG);
return new SnackbarUtils(mSnackbar).backColor(0XFF323232);
}
/**
* 初始化Snackbar實例
* 展示時間:Snackbar.LENGTH_INDEFINITE
* @param view
* @param message
* @return
*/
public static SnackbarUtils Indefinite(View view, String message){
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_INDEFINITE);
return new SnackbarUtils(mSnackbar).backColor(0XFF323232);
}
/**
* 初始化Snackbar實例
* 展示時間:duration 毫秒
* @param view
* @param message
* @param duration 展示時長(毫秒)
* @return
*/
public static SnackbarUtils Custom(View view, String message, int duration){
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_SHORT);
mSnackbar.setDuration(duration);
return new SnackbarUtils(mSnackbar).backColor(0XFF323232);
}
/**
* 設置mSnackbar背景色為 color_info
*/
public SnackbarUtils info(){
mSnackbar.getView().setBackgroundColor(color_info);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置mSnackbar背景色為 color_confirm
*/
public SnackbarUtils confirm(){
mSnackbar.getView().setBackgroundColor(color_confirm);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar背景色為 color_warning
*/
public SnackbarUtils warning(){
mSnackbar.getView().setBackgroundColor(color_warning);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar背景色為 color_warning
*/
public SnackbarUtils danger(){
mSnackbar.getView().setBackgroundColor(color_danger);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar背景色
* @param backgroundColor
*/
public SnackbarUtils backColor(@ColorInt int backgroundColor){
mSnackbar.getView().setBackgroundColor(backgroundColor);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置TextView(@+id/snackbar_text)的文字顏色
* @param messageColor
*/
public SnackbarUtils messageColor(@ColorInt int messageColor){
((TextView)mSnackbar.getView().findViewById(R.id.snackbar_text)).setTextColor(messageColor);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Button(@+id/snackbar_action)的文字顏色
* @param actionTextColor
*/
public SnackbarUtils actionColor(@ColorInt int actionTextColor){
((Button)mSnackbar.getView().findViewById(R.id.snackbar_action)).setTextColor(actionTextColor);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置 Snackbar背景色 + TextView(@+id/snackbar_text)的文字顏色 + Button(@+id/snackbar_action)的文字顏色
* @param backgroundColor
* @param messageColor
* @param actionTextColor
*/
public SnackbarUtils colors(@ColorInt int backgroundColor, @ColorInt int messageColor, @ColorInt int actionTextColor){
mSnackbar.getView().setBackgroundColor(backgroundColor);
((TextView)mSnackbar.getView().findViewById(R.id.snackbar_text)).setTextColor(messageColor);
((Button)mSnackbar.getView().findViewById(R.id.snackbar_action)).setTextColor(actionTextColor);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar 背景透明度
* @param alpha
* @return
*/
public SnackbarUtils alpha(float alpha){
alpha = alpha>=1.0f?1.0f:(alpha<=0.0f?0.0f:alpha);
mSnackbar.getView().setAlpha(alpha);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar顯示的位置
* @param gravity
*/
public SnackbarUtils gravityFrameLayout(int gravity){
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(mSnackbar.getView().getLayoutParams().width,mSnackbar.getView().getLayoutParams().height);
params.gravity = gravity;
mSnackbar.getView().setLayoutParams(params);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar顯示的位置,當Snackbar和CoordinatorLayout組合使用的時候
* @param gravity
*/
public SnackbarUtils gravityCoordinatorLayout(int gravity){
CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(mSnackbar.getView().getLayoutParams().width,mSnackbar.getView().getLayoutParams().height);
params.gravity = gravity;
mSnackbar.getView().setLayoutParams(params);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置按鈕文字內容 及 點擊監聽
* {@link Snackbar#setAction(CharSequence, View.OnClickListener)}
* @param resId
* @param listener
* @return
*/
public SnackbarUtils setAction(@StringRes int resId, View.OnClickListener listener){
return setAction(getSnackbar().getView().getResources().getText(resId), listener);
}
/**
* 設置按鈕文字內容 及 點擊監聽
* {@link Snackbar#setAction(CharSequence, View.OnClickListener)}
* @param text
* @param listener
* @return
*/
public SnackbarUtils setAction(CharSequence text, View.OnClickListener listener){
mSnackbar.setAction(text,listener);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置 mSnackbar 展示完成 及 隱藏完成 的監聽
* @param setCallback
* @return
*/
public SnackbarUtils setCallback(Snackbar.Callback setCallback){
mSnackbar.setCallback(setCallback);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置TextView(@+id/snackbar_text)左右兩側的圖片
* @param leftDrawable
* @param rightDrawable
* @return
*/
public SnackbarUtils leftAndRightDrawable(@Nullable @DrawableRes Integer leftDrawable, @Nullable @DrawableRes Integer rightDrawable){
Drawable drawableLeft = null;
Drawable drawableRight = null;
if(leftDrawable!=null){
try {
drawableLeft = getSnackbar().getView().getResources().getDrawable(leftDrawable.intValue());
}catch (Exception e){
Log.e("Jet","getSnackbar().getView().getResources().getDrawable(leftDrawable.intValue())");
}
}
if(rightDrawable!=null){
try {
drawableRight = getSnackbar().getView().getResources().getDrawable(rightDrawable.intValue());
}catch (Exception e){
Log.e("Jet","getSnackbar().getView().getResources().getDrawable(rightDrawable.intValue())");
}
}
return leftAndRightDrawable(drawableLeft,drawableRight);
}
/**
* 設置TextView(@+id/snackbar_text)左右兩側的圖片
* @param leftDrawable
* @param rightDrawable
* @return
*/
public SnackbarUtils leftAndRightDrawable(@Nullable Drawable leftDrawable, @Nullable Drawable rightDrawable){
TextView message = (TextView) mSnackbar.getView().findViewById(R.id.snackbar_text);
LinearLayout.LayoutParams paramsMessage = (LinearLayout.LayoutParams) message.getLayoutParams();
paramsMessage = new LinearLayout.LayoutParams(paramsMessage.width, paramsMessage.height,0.0f);
message.setLayoutParams(paramsMessage);
message.setCompoundDrawablePadding(message.getPaddingLeft());
int textSize = (int) message.getTextSize();
Log.e("Jet","textSize:"+textSize);
if(leftDrawable!=null){
leftDrawable.setBounds(0,0,textSize,textSize);
}
if(rightDrawable!=null){
rightDrawable.setBounds(0,0,textSize,textSize);
}
message.setCompoundDrawables(leftDrawable,null,rightDrawable,null);
LinearLayout.LayoutParams paramsSpace = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT,1.0f);
((Snackbar.SnackbarLayout)mSnackbar.getView()).addView(new Space(mSnackbar.getView().getContext()),1,paramsSpace);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置TextView(@+id/snackbar_text)中文字的對齊方式 居中
* @return
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public SnackbarUtils messageCenter(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
TextView message = (TextView) mSnackbar.getView().findViewById(R.id.snackbar_text);
//View.setTextAlignment需要SDK>=17
message.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY);
message.setGravity(Gravity.CENTER);
}
return new SnackbarUtils(mSnackbar);
}
/**
* 設置TextView(@+id/snackbar_text)中文字的對齊方式 居右
* @return
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public SnackbarUtils messageRight(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
TextView message = (TextView) mSnackbar.getView().findViewById(R.id.snackbar_text);
//View.setTextAlignment需要SDK>=17
message.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY);
message.setGravity(Gravity.CENTER_VERTICAL|Gravity.RIGHT);
}
return new SnackbarUtils(mSnackbar);
}
/**
* 向Snackbar布局中添加View(Google不建議,復雜的布局應該使用DialogFragment進行展示)
* @param layoutId 要添加的View的布局文件ID
* @param index
* @return
*/
public SnackbarUtils addView(int layoutId, int index) {
//加載布局文件新建View
View addView = LayoutInflater.from(mSnackbar.getView().getContext()).inflate(layoutId,null);
return addView(addView,index);
}
/**
* 向Snackbar布局中添加View(Google不建議,復雜的布局應該使用DialogFragment進行展示)
* @param addView
* @param index
* @return
*/
public SnackbarUtils addView(View addView, int index) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);//設置新建布局參數
//設置新建View在Snackbar內垂直居中顯示
params.gravity= Gravity.CENTER_VERTICAL;
addView.setLayoutParams(params);
((Snackbar.SnackbarLayout)mSnackbar.getView()).addView(addView,index);
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar布局的外邊距
* 注:經試驗發現,調用margins后再調用 gravityFrameLayout,則margins無效.
* 為保證margins有效,應該先調用 gravityFrameLayout,在 show() 之前調用 margins
* @param margin
* @return
*/
public SnackbarUtils margins(int margin){
return margins(margin,margin,margin,margin);
}
/**
* 設置Snackbar布局的外邊距
* 注:經試驗發現,調用margins后再調用 gravityFrameLayout,則margins無效.
* 為保證margins有效,應該先調用 gravityFrameLayout,在 show() 之前調用 margins
* @param left
* @param top
* @param right
* @param bottom
* @return
*/
public SnackbarUtils margins(int left, int top, int right, int bottom){
ViewGroup.LayoutParams params = mSnackbar.getView().getLayoutParams();
((ViewGroup.MarginLayoutParams) params).setMargins(left,top,right,bottom);
mSnackbar.getView().setLayoutParams(params);
return new SnackbarUtils(mSnackbar);
}
/**
* 經試驗發現:
* 執行過{@link SnackbarUtils#backColor(int)}后:background instanceof ColorDrawable
* 未執行過{@link SnackbarUtils#backColor(int)}:background instanceof GradientDrawable
* @return
*/
/*
public SnackbarUtils radius(){
Drawable background = mSnackbar.getView().getBackground();
if(background instanceof GradientDrawable){
Log.e("Jet","radius():GradientDrawable");
}
if(background instanceof ColorDrawable){
Log.e("Jet","radius():ColorDrawable");
}
if(background instanceof StateListDrawable){
Log.e("Jet","radius():StateListDrawable");
}
Log.e("Jet","radius()background:"+background.getClass().getSimpleName());
return new SnackbarUtils(mSnackbar);
}
*/
/**
* 通過SnackBar現在的背景,獲取其設置圓角值時候所需的GradientDrawable實例
* @param backgroundOri
* @return
*/
private GradientDrawable getRadiusDrawable(Drawable backgroundOri){
GradientDrawable background = null;
if(backgroundOri instanceof GradientDrawable){
background = (GradientDrawable) backgroundOri;
}else if(backgroundOri instanceof ColorDrawable){
int backgroundColor = ((ColorDrawable)backgroundOri).getColor();
background = new GradientDrawable();
background.setColor(backgroundColor);
}else {
}
return background;
}
/**
* 設置Snackbar布局的圓角半徑值
* @param radius 圓角半徑
* @return
*/
public SnackbarUtils radius(float radius){
//將要設置給mSnackbar的背景
GradientDrawable background = getRadiusDrawable(mSnackbar.getView().getBackground());
if(background != null){
radius = radius<=0?12:radius;
background.setCornerRadius(radius);
mSnackbar.getView().setBackgroundDrawable(background);
}
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar布局的圓角半徑值及邊框顏色及邊框寬度
* @param radius
* @param strokeWidth
* @param strokeColor
* @return
*/
public SnackbarUtils radius(int radius, int strokeWidth, @ColorInt int strokeColor){
//將要設置給mSnackbar的背景
GradientDrawable background = getRadiusDrawable(mSnackbar.getView().getBackground());
if(background != null){
radius = radius<=0?12:radius;
strokeWidth = strokeWidth<=0?1:(strokeWidth>=mSnackbar.getView().findViewById(R.id.snackbar_text).getPaddingTop()?2:strokeWidth);
background.setCornerRadius(radius);
background.setStroke(strokeWidth,strokeColor);
mSnackbar.getView().setBackgroundDrawable(background);
}
return new SnackbarUtils(mSnackbar);
}
/**
* 計算單行的Snackbar的高度值(單位 pix)
* @return
*/
private int calculateSnackBarHeight(){
int SnackbarHeight = ScreenUtil.dp2px(mSnackbar.getView().getContext(),28) + ScreenUtil.sp2px(mSnackbar.getView().getContext(),14);
Log.e("Jet","直接獲取MessageView高度:"+mSnackbar.getView().findViewById(R.id.snackbar_text).getHeight());
return SnackbarHeight;
}
/**
* 設置Snackbar顯示在指定View的上方
* 注:暫時僅支持單行的Snackbar,因為{@link SnackbarUtils#calculateSnackBarHeight()}暫時僅支持單行Snackbar的高度計算
* @param targetView 指定View
* @param contentViewTop Activity中的View布局區域 距離屏幕頂端的距離
* @param marginLeft 左邊距
* @param marginRight 右邊距
* @return
*/
public SnackbarUtils above(View targetView, int contentViewTop, int marginLeft, int marginRight){
marginLeft = marginLeft<=0?0:marginLeft;
marginRight = marginRight<=0?0:marginRight;
int[] locations = new int[2];
targetView.getLocationOnScreen(locations);
Log.e("Jet","距離屏幕左側:"+locations[0]+"==距離屏幕頂部:"+locations[1]);
int snackbarHeight = calculateSnackBarHeight();
Log.e("Jet","Snackbar高度:"+snackbarHeight);
//必須保證指定View的頂部可見 且 單行Snackbar可以完整的展示
if(locations[1] >= contentViewTop+snackbarHeight){
gravityFrameLayout(Gravity.BOTTOM);
ViewGroup.LayoutParams params = mSnackbar.getView().getLayoutParams();
((ViewGroup.MarginLayoutParams) params).setMargins(marginLeft,0,marginRight,mSnackbar.getView().getResources().getDisplayMetrics().heightPixels-locations[1]);
mSnackbar.getView().setLayoutParams(params);
}
return new SnackbarUtils(mSnackbar);
}
/**
* 設置Snackbar顯示在指定View的下方
* 注:暫時僅支持單行的Snackbar,因為{@link SnackbarUtils#calculateSnackBarHeight()}暫時僅支持單行Snackbar的高度計算
* @param targetView 指定View
* @param contentViewTop Activity中的View布局區域 距離屏幕頂端的距離
* @param marginLeft 左邊距
* @param marginRight 右邊距
* @return
*/
public SnackbarUtils bellow(View targetView, int contentViewTop, int marginLeft, int marginRight){
marginLeft = marginLeft<=0?0:marginLeft;
marginRight = marginRight<=0?0:marginRight;
int[] locations = new int[2];
targetView.getLocationOnScreen(locations);
int snackbarHeight = calculateSnackBarHeight();
int screenHeight = ScreenUtil.getScreenHeight(mSnackbar.getView().getContext());
//必須保證指定View的底部可見 且 單行Snackbar可以完整的展示
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
//為什么要'+2'? 因為在Android L(Build.VERSION_CODES.LOLLIPOP)以上,例如Button會有一定的'陰影(shadow)',陰影的大小由'高度(elevation)'決定.
//為了在Android L以上的系統中展示的Snackbar不要覆蓋targetView的陰影部分太大比例,所以人為減小2px的layout_marginBottom屬性.
if(locations[1]+targetView.getHeight()>=contentViewTop&&locations[1]+targetView.getHeight()+snackbarHeight+2<=screenHeight){
gravityFrameLayout(Gravity.BOTTOM);
ViewGroup.LayoutParams params = mSnackbar.getView().getLayoutParams();
((ViewGroup.MarginLayoutParams) params).setMargins(marginLeft,0,marginRight,screenHeight - (locations[1]+targetView.getHeight()+snackbarHeight+2));
mSnackbar.getView().setLayoutParams(params);
}
}else {
if(locations[1]+targetView.getHeight()>=contentViewTop&&locations[1]+targetView.getHeight()+snackbarHeight<=screenHeight){
gravityFrameLayout(Gravity.BOTTOM);
ViewGroup.LayoutParams params = mSnackbar.getView().getLayoutParams();
((ViewGroup.MarginLayoutParams) params).setMargins(marginLeft,0,marginRight,screenHeight - (locations[1]+targetView.getHeight()+snackbarHeight));
mSnackbar.getView().setLayoutParams(params);
}
}
return new SnackbarUtils(mSnackbar);
}
/**
* 顯示 mSnackbar
*/
public void show(){
if(mSnackbar!=null){
mSnackbar.show();
}
}
}
DEMO中還包括另一個工具類ScreenUtil,及測試Activity文件,詳情可查看GitHub:SnackbarUtils.歡迎大家提Bug,謹慎輕拍哈!
That's all !