①与Toast一样可以进行吐司信息;
②可以与用户交互操作;③一个时刻只能有唯一一个 Snackbar 显示。
我们先来感受一下它的强大吧,请看效果图:
上面的效果图代码实现:
import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.Button; public class MainActivity extends AppCompatActivity { private Button btn_show; private ViewGroup frameLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); } private void initViews() { frameLayout = (ViewGroup) findViewById(android.R.id.content); btn_show = (Button) findViewById(R.id.btn_show); btn_show.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view,"Hello SnackBar!",Snackbar.LENGTH_SHORT).show(); } }); } }其实使用的核心代码就一句:
Snackbar.make(view,"Hello SnackBar!",Snackbar.LENGTH_SHORT).show();使用起来类似于Toast,但也有与Toast的不同之处,Toast的使用:
Toast.makeText(MainActivity.this, "真的能够交互", Toast.LENGTH_SHORT).show();他们的第一个参数不同,Toast第一个参数是Context,而Snackbar第一个参数为View,这个View就是Toast的父布局。与Toast不同的是Snackbar显示时长有三种:
①Snackbar.LENGTH_SHORT:与Toast.LENGHT_SHORT(大约1.x秒)一样显示较短时长后自动消失。
②Snackbar.LENGTH_LONG:与Toast.LENGHT_LONG(大约3秒)一样显示相对较长时间后自动消失。
③Snackbar.LENGTH_INDEFINITE:永不消失除非手动调用dismiss()方法去除Snackbar。
刚才已经说过Snackbar比Toast强大,其中有一点就是Snackbar可以用来与用户交互,我们看一下效果:
核心代码:
final Snackbar snackbar = Snackbar.make(frameLayout, "Hello SnackBar!", Snackbar.LENGTH_INDEFINITE); snackbar.setAction("OK?", new View.OnClickListener() { @Override public void onClick(View view) { // snackbar.dismiss(); //doSomething } }); snackbar.show();setAction(CharSequence text, View.OnClickListener listener)方法为用户交互的点击事件,第一个参数是点击事件的信息,第二个就是一个View.OnClickListener点击监听器。
Snackbar可以反馈指的是它可以监听Snackbar的显示与退出(也包括各种退出的类型的监听),来张效果图:
核心代码:
final Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "反馈监听的Snackbar", Snackbar.LENGTH_LONG); snackbar.setAction("能交互吗", new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "真的能够交互", Toast.LENGTH_SHORT).show(); } }); snackbar.setCallback(new Snackbar.Callback() { @Override public void onDismissed(Snackbar snackbar, int event) { super.onDismissed(snackbar, event); switch (event) { case Snackbar.Callback.DISMISS_EVENT_ACTION: Toast.makeText(MainActivity.this, "Snackbar通过Action的点击事件退出", Toast.LENGTH_SHORT).show(); break; case Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE: Toast.makeText(MainActivity.this, "Snackbar由于新的Snackbar显示而退出", Toast.LENGTH_SHORT).show(); break; case Snackbar.Callback.DISMISS_EVENT_MANUAL: Toast.makeText(MainActivity.this, "Snackbar通过调用dismiss()方法退出", Toast.LENGTH_SHORT).show(); break; case Snackbar.Callback.DISMISS_EVENT_SWIPE: Toast.makeText(MainActivity.this, "Snackbar右划退出", Toast.LENGTH_SHORT).show(); break; case Snackbar.Callback.DISMISS_EVENT_TIMEOUT: Toast.makeText(MainActivity.this, "Snackbar自然退出", Toast.LENGTH_SHORT).show(); break; } } @Override public void onShown(Snackbar snackbar) { super.onShown(snackbar); Toast.makeText(MainActivity.this, "Snackbar显示", Toast.LENGTH_SHORT).show(); } }); snackbar.show();
①Snackbar.Callback.DISMISS_EVENT_SWIPE=0:Indicates that the Snackbar was dismissed via a swipe。
该事件下Snackbar退出的原因是;意思是“Snackbar右划退出事件”,注意这个右划事件只有在父布局为CoordinatorLayout才会被触发,其他布局如LinearLayout、RelativeLayout等是不会出发的。
②Snackbar.Callback.DISMISS_EVENT_ACTION = 1:Indicates that the Snackbar was dismissed via an action click。
该事件下Snackbar退出的原因是:Snackbar通过Action点击事件触发退出。
③Snackbar.Callback.DISMISS_EVENT_TIMEOUT = 2:Indicates that the Snackbar was dismissed via a timeout。
该事件下Snackbar退出的原因是:Snackbar超出通过setDuration()方法所设置的规定时间后退出,这也是正常的自然退出。
④Snackbar.Callback.DISMISS_EVENT_MANUAL = 3:Indicates that the Snackbar was dismissed via a call to dismiss()。
该事件下Snackbar提出的原因是:通过手动调用了dismiss()方法。
⑤Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE = 4:Indicates that the Snackbar was dismissed from a new Snackbar being shown。
该事件下Snackbar提出的原因:由于新的Snackbar的显示而退出。
在自定义Snackbar之前需要弄清Snackbar的内部构造,打开Snackbar源码可以看到其有一个内部类Snackbar.SnackbarLayout,该内部类就是Snackbar布局,然后找到Snackbar.SnackbarLayout的布局资源文件design_layout_snackbar_include.xml,打开该文件看到一个TextView(吐司左边的信息)和一个Button(显示SnackBar右侧与用户交互的按钮)。
<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= "@dimen/design_snackbar_padding_vertical" android:paddingBottom= "@dimen/design_snackbar_padding_vertical" android:paddingLeft= "@dimen/design_snackbar_padding_horizontal" android:paddingRight= "@dimen/design_snackbar_padding_horizontal" android:textAppearance= "@style/TextAppearance.Design.Snackbar.Message" android:maxLines= "@integer/design_snackbar_text_max_lines" 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= "@dimen/design_snackbar_extra_spacing_horizontal" android:layout_marginStart= "@dimen/design_snackbar_extra_spacing_horizontal" android:layout_gravity= "center_vertical|right|end" android:paddingTop= "@dimen/design_snackbar_padding_vertical" android:paddingBottom= "@dimen/design_snackbar_padding_vertical" android:paddingLeft= "@dimen/design_snackbar_padding_horizontal" android:paddingRight= "@dimen/design_snackbar_padding_horizontal" android:visibility= "gone" android:textColor= "?attr/colorAccent" style= "?attr/borderlessButtonStyle"/> </merge>如果我们能够获取到该View岂不是就能随意的更改一些Snackbar的背景样式、吐司信息、吐司文字的颜色及大小、交互按钮Button的颜色与文字大小等信息了吗?不错,Snackbar提供了一个getView()方法,这个方法就可以获取到Snackbar.SnackbarLayout的实例。那么下面就来尝试一下。
核心代码:
final Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "自定义Snackbar", Snackbar.LENGTH_LONG); snackbar.setAction("是否OK", new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "真的能够交互", Toast.LENGTH_SHORT).show(); } }); final Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) snackbar.getView(); if (snackbarView != null){ //设置SnackBar背景与透明度 snackbarView.setBackgroundResource(R.drawable.background); snackbarView.setAlpha((float) 0.4); //设置Action的字体颜色与大小 final Button snackbar_action = (Button) snackbarView.findViewById(android.support.design.R.id.snackbar_action); snackbar_action.setTextColor(Color.RED); snackbar_action.setTextSize(convertSpToPixel(snackbarView.getContext(),10)); //这是Text的文字颜色与大小 final TextView snackbar_text = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text); snackbar_text.setTextColor(Color.RED); snackbar_text.setTextSize(convertSpToPixel(snackbarView.getContext(),10)); //设置左侧icon Drawable drawable = ContextCompat.getDrawable(MainActivity.this, R.mipmap.ic_core); if (drawable != null) { drawable = fitDrawable(getResources(), drawable, (int) convertDpToPixel(24, MainActivity.this)); } else { throw new IllegalArgumentException("resource_id is not a valid drawable!"); } final Drawable[] compoundDrawables = snackbar_text.getCompoundDrawables(); snackbar_text.setCompoundDrawables(drawable, compoundDrawables[1], compoundDrawables[2], compoundDrawables[3]); } snackbar.show();对于自定义我们首先得获取Snackbar的布局:
final Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) snackbar.getView();
拿到布局以后我们可以对Snackbar的背景和透明度进行设置:
snackbarView.setBackgroundResource(R.drawable.background); snackbarView.setAlpha((float) 0.4);还记得刚才我们看的Snackbar的布局文件吗?里面就是一个TextView和一个Button,如果我们想要对他们进行自定义我们可以先拿到他们的布局view,首先我们看一下Button:
final Button snackbar_action = (Button) snackbarView.findViewById(android.support.design.R.id.snackbar_action);我们拿到Button实例之后就可以对其进行自定义了:
snackbar_action.setTextColor(Color.RED); snackbar_action.setTextSize(convertSpToPixel(snackbarView.getContext(),10));我们在对Snackbar里的TextView进行自定义:
final TextView snackbar_text = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);对其设置字体大小和字体颜色:
snackbar_text.setTextColor(Color.RED); snackbar_text.setTextSize(convertSpToPixel(snackbarView.getContext(),10));对于TextView本身来说还有一个方法就是可以通过setCompoundDrawables(left,top,right,bottom)设置图标Icon,现在我们对Snackbar里的TextView进行左侧图标Icon的设置:
//设置左侧icon Drawable drawable = ContextCompat.getDrawable(MainActivity.this, R.mipmap.ic_core); if (drawable != null) { drawable = fitDrawable(getResources(), drawable, (int) convertDpToPixel(24, MainActivity.this)); } else { throw new IllegalArgumentException("resource_id is not a valid drawable!"); } final Drawable[] compoundDrawables = snackbar_text.getCompoundDrawables(); snackbar_text.setCompoundDrawables(drawable, compoundDrawables[1], compoundDrawables[2], compoundDrawables[3]);这里的代码还用到的方法如下:
/** * 方法描述:将drawable压缩为指定宽高的drawable * * @param resources * @param drawable 原始drawable * @param sizePx 指定的drawable压缩宽高 * @return */ private static Drawable fitDrawable(Resources resources, Drawable drawable, int sizePx) { if (drawable.getIntrinsicWidth() != sizePx || drawable.getIntrinsicHeight() != sizePx) { if (drawable instanceof BitmapDrawable) { drawable = new BitmapDrawable(resources, Bitmap.createScaledBitmap(getBitmap(drawable), sizePx, sizePx, true)); } } drawable.setBounds(0, 0, sizePx, sizePx); return drawable; } /** * 方法描述:将Drawable转化为Bitmap * * @param drawable * @return */ private static Bitmap getBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } else if (drawable instanceof VectorDrawable) { return getBitmap((VectorDrawable) drawable); } else { throw new IllegalArgumentException("unsupported drawable type"); } } /** * 方法描述:将VectorDrawable转化为Bitmap * * @param vectorDrawable * @return */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static Bitmap getBitmap(VectorDrawable vectorDrawable) { Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); vectorDrawable.draw(canvas); return bitmap; }好了,Snackbar的基本用法已经差不多了,现在封装一个Snackbar的工具类吧!!!
import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; import android.os.Build; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.content.ContextCompat; import android.util.TypedValue; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * 类描述:Snackbar工具类 * Created by lizhenya on 16/7/14. */ public class SnackbarUtil { private static Snackbar.SnackbarLayout getSnackbarLayout(Snackbar snackbar) { final Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) snackbar.getView(); return snackbarView; } public static Snackbar shortMake(View view, CharSequence message) { final Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT); return snackbar; } public static Snackbar shortMake(View view, int messageId) { final Snackbar snackbar = Snackbar.make(view, messageId, Snackbar.LENGTH_LONG); return snackbar; } public static Snackbar longMake(View view, CharSequence message) { final Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT); return snackbar; } public static Snackbar longMake(View view, int messageId) { final Snackbar snackbar = Snackbar.make(view, messageId, Snackbar.LENGTH_LONG); return snackbar; } public static Snackbar indefiniteMake(View view, CharSequence message) { final Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE); return snackbar; } public static Snackbar indefiniteMake(View view, int messageId) { final Snackbar snackbar = Snackbar.make(view, messageId, Snackbar.LENGTH_INDEFINITE); return snackbar; } /** * 方法描述:设置Snackbar的透明度 * * @param snackbar * @param alpha */ public static void setSnackbarAlpha(Snackbar snackbar, float alpha) { final Snackbar.SnackbarLayout snackbarView = getSnackbarLayout(snackbar); if (snackbarView == null) return; snackbarView.setAlpha(alpha); } /** * 方法描述:设置Snackbar的背景颜色 * * @param snackbar * @param color */ public static void setSnackbarBackgroundColor(Snackbar snackbar, int color) { final Snackbar.SnackbarLayout snackbarView = getSnackbarLayout(snackbar); if (snackbarView == null) return; snackbarView.setBackgroundColor(color); } /** * 方法描述:设置Snackbar的背景图片资源 * * @param snackbar * @param resId */ public static void setSnackbarBackgroudResource(Snackbar snackbar, int resId) { final Snackbar.SnackbarLayout snackbarView = getSnackbarLayout(snackbar); if (snackbarView == null) return; snackbarView.setBackgroundResource(resId); } /** * 方法描述:设置Snackbar中Action的字体颜色 * * @param snackbar * @param color */ public static void setActionTextColor(Snackbar snackbar, int color) { //snackbar.setActionTextColor(color); final Snackbar.SnackbarLayout snackbarView = getSnackbarLayout(snackbar); if (snackbarView == null) return; final Button snackbar_action = (Button) snackbarView.findViewById(android.support.design.R.id.snackbar_action); snackbar_action.setTextColor(color); } /** * 方法描述:设置Snackbar中Action的字体大小 * * @param snackbar * @param size Action的字体大小(单位为sp) */ public static void setActionTextSize(Snackbar snackbar, float size) { final Snackbar.SnackbarLayout snackbarView = getSnackbarLayout(snackbar); if (snackbarView == null) return; final Button snackbar_action = (Button) snackbarView.findViewById(android.support.design.R.id.snackbar_action); snackbar_action.setTextSize(sp2px(snackbarView.getContext(), size)); } /** * 方法描述:设置SnackBar左侧TextView控件的文字颜色和大小 * * @param snackbar * @param color TextView控件的文字颜色 * @param size TextView控件的文字大小 */ public static void setTextColorAndSize(Snackbar snackbar, @Nullable int color, @Nullable float size) { final Snackbar.SnackbarLayout snackbarView = getSnackbarLayout(snackbar); if (snackbarView == null) return; final TextView snackbar_text = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text); if (color != 0) { snackbar_text.setTextColor(color); } if (size != 0) { snackbar_text.setTextSize(size); } } /** * 方法描述:在Snackbar左侧添加icon * * @param snackbar Snackbar实例 * @param drawableResId 添加的icon资源ID * @param sizeDp icon的宽度与高度值 */ public static void setIconLeft(Snackbar snackbar, int drawableResId, float sizeDp) { final Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) getSnackbarLayout(snackbar); if (snackbarView == null) return; //snackbar不同于Toast,snackbar依赖于Activity而存在 final Context mContext = snackbarView.getContext(); if (mContext == null) { return; } final TextView snackbar_text = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text); Drawable drawable = ContextCompat.getDrawable(mContext, drawableResId); if (drawable != null) { drawable = fitDrawable(mContext.getResources(), drawable, (int) convertDpToPixel(sizeDp, mContext)); } else { throw new IllegalArgumentException("resource_id is not a valid drawable!"); } final Drawable[] compoundDrawables = snackbar_text.getCompoundDrawables(); snackbar_text.setCompoundDrawables(drawable, compoundDrawables[1], compoundDrawables[2], compoundDrawables[3]); } /** * 方法描述:将drawable压缩为指定宽高的drawable * * @param resources * @param drawable 原始drawable * @param sizePx 指定的drawable压缩宽高 * @return */ private static Drawable fitDrawable(Resources resources, Drawable drawable, int sizePx) { if (drawable.getIntrinsicWidth() != sizePx || drawable.getIntrinsicHeight() != sizePx) { if (drawable instanceof BitmapDrawable) { drawable = new BitmapDrawable(resources, Bitmap.createScaledBitmap(getBitmap(drawable), sizePx, sizePx, true)); } } drawable.setBounds(0, 0, sizePx, sizePx); return drawable; } /** * 方法描述:将Drawable转化为Bitmap * * @param drawable * @return */ private static Bitmap getBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } else if (drawable instanceof VectorDrawable) { return getBitmap((VectorDrawable) drawable); } else { throw new IllegalArgumentException("unsupported drawable type"); } } /** * 方法描述:将VectorDrawable转化为Bitmap * * @param vectorDrawable * @return */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static Bitmap getBitmap(VectorDrawable vectorDrawable) { Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); vectorDrawable.draw(canvas); return bitmap; } /** * 方法描述:dp转化为px * * @param dpValue * @param context * @return */ private static float convertDpToPixel(float dpValue, Context context) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics()); } /** * sp转px * * @param context * @param spVal * @return */ public static int convertSpToPixel(Context context, float spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources().getDisplayMetrics()); } public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } }
文章最后介绍一个第三方框架。系统默认的Snackbar是从屏幕底部弹出的,如果想要改变其弹出位置可以将其父控件置于想要弹出的屏幕位置,但是即使这样做也不能改变动画,现在介绍一个从顶部弹出的Snackbar框架TSnackbar,github链接:https://github.com/AndreiD/TSnackBar。
Snackbar使用源码下载:http://download.csdn.net/detail/u012810020/9594379