了不起的厂长 2019-06-28
1.最简单创造方法
2.源码分析
3.经典总结
4.思考问题分析
Snackbar sb = Snackbar.make(v,"潇湘剑雨",Snackbar.LENGTH_LONG)
.setAction("删除吗?", new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击了"是吗?"字符串操作
ToastUtils.showRoundRectToast("逗比");
}
})
.setActionTextColor(Color.RED)
.setText("杨充是个逗比")
.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
super.onDismissed(transientBottomBar, event);
switch (event) {
case Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE:
case Snackbar.Callback.DISMISS_EVENT_MANUAL:
case Snackbar.Callback.DISMISS_EVENT_SWIPE:
case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
ToastUtils.showRoundRectToast("删除成功");
break;
case Snackbar.Callback.DISMISS_EVENT_ACTION:
ToastUtils.showRoundRectToast("撤销了删除操作");
break;
}
Log.d("MainActivity","onDismissed");
}
@Override
public void onShown(Snackbar transientBottomBar) {
super.onShown(transientBottomBar);
Log.d("MainActivity","onShown");
}
});
sb.show();Snackbar显示只有一种方式,那就是调用show()方法,但是消失有几种方式:时间到了自动消失、点击了右侧按钮消失、新的Snackbar出现导致旧的Snackbar消失、滑动消失或者通过调用dismiss()消失。
分别对应于Snackbar.Callback中的几个常量值。
Callback有两个方法
创建Snackbar需要使用静态的make方法,并且其中的view参数是一个查找父布局的起点

其中findSuitableParent()方法为以view为起点寻找合适的父布局,下面看看findSuitableParent()如何做的?

2.2.1 setActionTextColor设置action颜色
@NonNull
public Snackbar setActionTextColor(@ColorInt int color) {
final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
final TextView tv = contentLayout.getActionView();
tv.setTextColor(color);
return this;
}
//然后看SnackbarContentLayout类中getActionView方法
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMessageView = (TextView) findViewById(R.id.snackbar_text);
mActionView = (Button) findViewById(R.id.snackbar_action);
}
public Button getActionView() {
return mActionView;
}2.2.2 看setAction()方法的实现
@NonNull
public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
final TextView tv = contentLayout.getActionView();
if (TextUtils.isEmpty(text) || listener == null) {
tv.setVisibility(View.GONE);
tv.setOnClickListener(null);
} else {
tv.setVisibility(View.VISIBLE);
tv.setText(text);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onClick(view);
// Now dismiss the Snackbar
dispatchDismiss(BaseCallback.DISMISS_EVENT_ACTION);
}
});
}
return this;
}2.3.1 show显示
public void show() {
SnackbarManager.getInstance().show(mDuration, mManagerCallback);
}
public void show(int duration, Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// 表示回调已在队列中。我们只需更新持续时间
mCurrentSnackbar.duration = duration;
// 如果这是当前正在显示的Snackbar,请调用重新调度它的
// timeout
mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
// 这个方法很重要,当执行时间结束后,就会自动dismiss。下面再详细分析
scheduleTimeoutLocked(mCurrentSnackbar);
return;
} else if (isNextSnackbarLocked(callback)) {
//我们只需更新持续时间
mNextSnackbar.duration = duration;
} else {
//否则,我们需要创建一个新记录并对其进行排队。
mNextSnackbar = new SnackbarRecord(duration, callback);
}
if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
// 如果我们目前有一个Snackbar,请尝试取消它并排队等待。
return;
} else {
// 清除当前的快捷键
mCurrentSnackbar = null;
//很重要
showNextSnackbarLocked();
}
}
}
//注意这个callback方法
final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
@Override
public void show() {
sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, BaseTransientBottomBar.this));
}
@Override
public void dismiss(int event) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0,
BaseTransientBottomBar.this));
}
};
//处理sHandler发送的消息
static {
sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(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;
}
return false;
}
});
}private void showNextSnackbarLocked() {
if (mNextSnackbar != null) {
mCurrentSnackbar = mNextSnackbar;
mNextSnackbar = null;
final Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
} else {
// The callback doesn't exist any more, clear out the Snackbar
mCurrentSnackbar = null;
}
}
}2.3.2 看看scheduleTimeoutLocked源码如何销毁snackBar
private void scheduleTimeoutLocked(SnackbarRecord r) {
if (r.duration == Snackbar.LENGTH_INDEFINITE) {
// If we're set to indefinite, we don't want to set a timeout
return;
}
int durationMs = LONG_DURATION_MS;
if (r.duration > 0) {
durationMs = r.duration;
} else if (r.duration == Snackbar.LENGTH_SHORT) {
durationMs = SHORT_DURATION_MS;
}
mHandler.removeCallbacksAndMessages(r);
mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_TIMEOUT, r), durationMs);
}
//接受mHandler消息并且处理
mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_TIMEOUT:
handleTimeout((SnackbarRecord) message.obj);
return true;
}
return false;
}
});
//
void handleTimeout(SnackbarRecord record) {
synchronized (mLock) {
if (mCurrentSnackbar == record || mNextSnackbar == record) {
cancelSnackbarLocked(record, Snackbar.Callback.DISMISS_EVENT_TIMEOUT);
}
}
}
//最终可以追踪到这个方法
private boolean cancelSnackbarLocked(SnackbarRecord record, int event) {
final Callback callback = record.callback.get();
if (callback != null) {
// Make sure we remove any timeouts for the SnackbarRecord
mHandler.removeCallbacksAndMessages(record);
callback.dismiss(event);
return true;
}
return false;
}在显示的时候是这样设置动画的,具体如下所示

在隐藏的时候是这样设置动画的,具体如下所示

最后具体看一下animateViewOut部分源码
private void animateViewOut(final int event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.animate(mView)
.translationY(mView.getHeight())
.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
.setDuration(ANIMATION_DURATION)
.setListener(new ViewPropertyAnimatorListenerAdapter() {
@Override
public void onAnimationStart(View view) {
mContentViewCallback.animateContentOut(0, ANIMATION_FADE_DURATION);
}
@Override
public void onAnimationEnd(View view) {
onViewHidden(event);
}
}).start();
} else {
Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
R.anim.design_snackbar_out);
anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
anim.setDuration(ANIMATION_DURATION);
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
onViewHidden(event);
}
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
});
mView.startAnimation(anim);
}
}onViewHidden提供具体的业务处理,具体如下所示

Snackbar负责显示和消失,具体来说其实就是添加和移除View的过程。Snackbar和SnackbarManager的设计很巧妙,利用一个SnackbarRecord对象保存Snackbar的显示时间以及SnackbarManager.Callback对象,前面说到每一个Snackbar都有一个叫做mManagerCallback的SnackbarManager.Callback对象,下面看一下SnackRecord类的定义:

为什么CoordinatorLayout + FloatingActionButton,当Snackbar显示的时候FloatingActionButton会上移呢,这个是怎么实现的?
至于说Snackbar控件show时为何从下往上移出来,看下面这段代码就知道呢,如下所示

直接找到make方法中的填充布局,然后去看design_layout_snackbar_include的布局参数,结果如下:

与Toast进行比较,SnackBar有优势:

可以一行代码调用,也可以自己使用链式编程调用。支持设置显示时长属性;可以设置背景色;可以设置文字大小,颜色;可以设置action内容,文字大小,颜色,还有点击事件;可以设置icon;代码如下所示,更多内容可以直接运行demo哦!
//1.只设置text
SnackBarUtils.showSnackBar(this,"滚犊子");
//2.设置text,action,和点击事件
SnackBarUtils.showSnackBar(this, "滚犊子", "ACTION", new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtils.showRoundRectToast("滚犊子啦?");
}
});
//3.设置text,action,和点击事件,和icon
SnackBarUtils.showSnackBar(this, "滚犊子", "ACTION",R.drawable.icon_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtils.showRoundRectToast("滚犊子啦?");
}
});
//4.链式调用
SnackBarUtils.builder()
.setBackgroundColor(this.getResources().getColor(R.color.color_7f000000))
.setTextSize(14)
.setTextColor(this.getResources().getColor(R.color.white))
.setTextTypefaceStyle(Typeface.BOLD)
.setText("滚犊子")
.setMaxLines(4)
.centerText()
.setActionText("收到")
.setActionTextColor(this.getResources().getColor(R.color.color_f25057))
.setActionTextSize(16)
.setActionTextTypefaceStyle(Typeface.BOLD)
.setActionClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtils.showRoundRectToast("滚犊子啦?");
}
})
.setIcon(R.drawable.icon_cancel)
.setActivity(MainActivity.this)
.setDuration(SnackBarUtils.DurationType.LENGTH_INDEFINITE)
.build()
.show();