Material轻量级反馈:Snackbar
了解Material Design的开发者对Snackbar不陌生,这是和Toast一样的轻量级反馈。关于Toast详见Toast:前后台弹出Toast不一致?。
1、Snackbar
Snackbar和Toast类似,方法也都差不多。Snackbar继承自BaseTransientBottomBar。
1.1、使用Snackbar
Snackbar不是系统自带的,Toast自API 1就跟随系统。要使用Snackbar在MDC库中需要额外添加依赖:
implementation 'com.google.android.material:material:1.2.1'1.2、创建Snackbar
Snackbar对象只能通过Snackbar类提供的两个static方法make(...)创建,因为Snackbar的构造函数是private私有的
private Snackbar(ViewGroup parent,View content,
com.google.android.material.snackbar.ContentViewCallback contentViewCallback) {
super(parent, content, contentViewCallback);
accessibilityManager =
(AccessibilityManager) parent.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
}①static Snackbar make(View view,CharSequence text,int duration):内部创建Layout,并设置传入的参数值。
②static Snackbar make(View view, @StringRes int resId,int duration):调用上面的方法
public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
return make(view, view.getResources().getText(resId), duration);
}1.3、显示Snackbar
了解一下显示Snackbar的流程。
public void show() {
super.show();
}调用父类BaseTransientBottomBar的show()方法
public void show() {
SnackbarManager.getInstance().show(getDuration(), managerCallback);
}没想到是用SnackbarManager来管理Snackbar,而且还是单例模式
...
private static SnackbarManager snackbarManager;
//单例
static SnackbarManager getInstance() {
if (snackbarManager == null) {
snackbarManager = new SnackbarManager();
}
return snackbarManager;
}
//私有构造方法
private SnackbarManager() {
lock = new Object();
handler =
new Handler(
Looper.getMainLooper(),
new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
switch (message.what) {
case MSG_TIMEOUT:
handleTimeout((SnackbarRecord) message.obj);
return true;
default:
return false;
}
}
});
}看看这个Snackbar管理器是如何show() Snackbar的:
public void show(int duration, Callback callback) {
synchronized (lock) {
if (isCurrentSnackbarLocked(callback)) {
// Means that the callback is already in the queue. We'll just update the duration
currentSnackbar.duration = duration;
// If this is the Snackbar currently being shown, call re-schedule it's
// timeout
handler.removeCallbacksAndMessages(currentSnackbar);
scheduleTimeoutLocked(currentSnackbar);
return;
} else if (isNextSnackbarLocked(callback)) {
// We'll just update the duration
nextSnackbar.duration = duration;
} else {
// Else, we need to create a new record and queue it
nextSnackbar = new SnackbarRecord(duration, callback);
}
if (currentSnackbar != null
&& cancelSnackbarLocked(currentSnackbar, Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
// If we currently have a Snackbar, try and cancel it and wait in line
return;
} else {
// Clear out the current snackbar
currentSnackbar = null;
// Otherwise, just show it now
showNextSnackbarLocked();
}
}
}具体的方法不用细看,有一个callback回调和一个Handler。其中的Handler在SnackbarManager私有构造函数中使用MainLooper初始化
interface Callback {
void show();
void dismiss(int event);
}而这个传入的SnackbarManager.Callback回调是在Snackbar的父类BaseTransientBottomBar初始化的成员变量:
SnackbarManager.Callback managerCallback =
new SnackbarManager.Callback() {
@Override
public void show() {
handler.sendMessage(handler.obtainMessage(MSG_SHOW, BaseTransientBottomBar.this));
}
@Override
public void dismiss(int event) {
handler.sendMessage(
handler.obtainMessage(MSG_DISMISS, event, 0, BaseTransientBottomBar.this));
}
};BaseTransientBottomBar中定义了静态的handler,在类加载的时候初始化,用来show()和dismiss() Snackbar。
static {
handler =
new Handler(
Looper.getMainLooper(),
new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
switch (message.what) {
case MSG_SHOW:
((BaseTransientBottomBar) message.obj).showView();
return true;
case MSG_DISMISS:
((BaseTransientBottomBar) message.obj).hideView(message.arg1);
return true;
default:
return false;
}
}
});
}同样dissmiss()方法的执行流程也是一样的。
2、方法
了解一下Snackbar还有哪些常用的方法。可以看见这些方法都返回调用对象自身,方便优美的链式调用一气呵成,完成设置,最后show出来。
①Snackbar setAction(@StringRes int resId, View.OnClickListener listener):设置点击动作
②Snackbar setAction(CharSequence text,View.OnClickListener listener)
③Snackbar setActionTextColor(ColorStateList colors)
④Snackbar setActionTextColor(@ColorInt int color)
⑤Snackbar setBackgroundTint(@ColorInt int color)
⑥Snackbar setBackgroundTintList(ColorStateList colorStateList)
⑦Snackbar setBackgroundTintMode(PorterDuff.Mode mode)
⑧Snackbar setMaxInlineActionWidth(@Dimension int width)
⑨Snackbar setText(CharSequence message)
⑩Snackbar setText(@StringRes int resId)
⑪Snackbar setTextColor(ColorStateList colors)
⑫Snackbar setTextColor(@ColorInt int color)
⑬boolean isShown():当前Snackbar是否显示
3、示例
看再多源码,不如一试。
3.1、默认Snackbar
使用Material提供的Snackbar很简单,一行代码搞定!
Snackbar.make(v, R.string.toast, 500).show();
3.2、定制Snackbar
创建Snackbar实例,先不展示
Snackbar snackbar = Snackbar.make(v, R.string.news_delete_toast, 750);获取Snackbar中的基础BaseTransientBottomBar.SnackbarBaseLayout布局
View view = snackbar.getView();进行设置,还可以拿到其中的TextView控件
view.setBackgroundColor(Color.GRAY);
TextView textView = view.findViewById(R.id.snackbar_text);
textView.setTextColor(Color.BLUE);
snackbar.setAction("取消", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});设置完成最后再显示即可
snackbar.show();
相关资料:
Docs > Reference > Snackbar">Android Developers > Docs > Reference > Snackbar

