什么是Snackbar?
Google退出Matrial Design已经有一段时间了,其中有一款控件,叫做Snackbar。
那么它是做什么的呢?开发过Android的童鞋们应该对Toast不陌生了,Toast是一款用于在屏幕上显示提示信息的控件。而Snackbar,可以理解为Matrial Design风格的Toast,并且在功能上也有了一定的加强。
废话不多说,上图:
图中屏幕下方弹出的框就是Snackbar。其有着与Toast一样的特性(定时隐藏),也有一些新的特性(可按钮,不点击后隐藏等)。
使用Snackbar
Snackbar的用法并不复杂,使用时并不需要手动去创建Snackbar对象,其用法与Toast相似:
Snackbar.make(view,"显示的内容", Snackbar.LENGTH_LONG).show();
而如果要添加一个按钮并增加点击事件,只需要使用setAction方法即可。
Snackbar中的按钮被点击后,Snackbar会自动消失,并响应//点击事件:
Snackbar.make(view, "显示的内容", Snackbar.LENGTH_LONG)
.setAction("按钮", new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO: 点击按钮后的处理
}
}).show();
当然,Snackbar的每一种属性都可以单独设置,不过其构造方法是私有的,所以获取Snackbar对象的途径只有Snackbar.make方法:
//view可以为布局中任何控件或者跟布局。
public static Snackbar make(@NonNull View view, @NonNull CharSequence text, int duration)
public class MainActivity extends AppCompatActivity {
Snackbar snackbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FloatingActionButton fab = findViewById(R.id.fab);
snackbar = Snackbar.make(fab, "显示的内容", Snackbar.LENGTH_LONG)
.setAction("按钮", new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO: 点击按钮后的处理
}
});
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
snackbar.setText("修改后的内容");
snackbar.setAction("修改后的按钮文字", new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO: 点击按钮后的处理
}
});
snackbar.show();
}
});
}
}
源码分析
那么了解了Snackbar的用法,大家可能依然会有许多疑问,比如:
- Snackbar是如何显示到界面上的?
- 多个Snackbar短时间内显示的时候排队逻辑时什么样的?
- Snackbar的make方法为什么必须要传递一个view进去?
- ····
好的,那么我们就通过源码来了解一下上述疑问。
Snackbar如何显示在界面上
首先,我们需要知道,我们调用Snackbar的make方法后,再次调用show方法,即可将其显示到屏幕上。所以我们就从这两个方法入手:
make方法(ctrl+鼠标左键点进去):
@NonNull
public static Snackbar make(@NonNull View view, @NonNull CharSequence text, int duration) {
ViewGroup parent = findSuitableParent(view);
if (parent == null) {
throw new IllegalArgumentException("No suitable parent found from the given view. Please provide a valid view.");
} else {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
SnackbarContentLayout content = (SnackbarContentLayout)inflater.inflate(hasSnackbarButtonStyleAttr(parent.getContext()) ? layout.mtrl_layout_snackbar_include : layout.design_layout_snackbar_include, parent, false);
Snackbar snackbar = new Snackbar(parent, content, content);
snackbar.setText(text);
snackbar.setDuration(duration);
return snackbar;
}
}
从Snackbar的make方法可以看到,其通过我们传递的view
来获取到了一个ViewGroup parent
。之后又通过parent
创建了LayoutInflater
并取得了SnackbarContentLayout content
。接下来内部通过new创建了一个Snackbar
实例,并且设置显示的文字及显示时长。最终将设置好的Snackbar
返回。
看到这里我们可以确定每次调用Snackbar
就会新建一个Snackbar
对象。而其他代码的意义却不明朗。没关系,继续分析。
首先,我们看看第一行代码:ViewGroup parent = findSuitableParent(view);
。可以推测,findSuitableParent
方法通过view
找到了一个ViewGroup
。进入方法查看:
private static ViewGroup findSuitableParent(View view) {
ViewGroup fallback = null;
do {
if (view instanceof CoordinatorLayout) {
return (ViewGroup)view;
}
if (view instanceof FrameLayout) {
if (view.getId() == 16908290) {
return (ViewGroup)view;
}