在项目中肯定有不少地方用到Toast,但是在项目各种各样的需求上,Toast已经不能满足我们的需求了。
其实google在Android 5.0的时候就推出了Snackbar,它算是Toast的一个进阶控件。
它是Material Design中的一个控件,与Toast的最大区别就是它可以与用户进行交互–也就是说它可以处理用户的点击事件。
它需要配合CoordinatorLayout使用,可以在xml中写个CoordinatorLayout也可以使用下面的代码获取CoordinatorLayout
View decorView = getWindow().getDecorView();
CoordinatorLayout coordinatorLayout = decorView.findViewById(R.id.content);
简单用法
Snackbar.make(coordinatorLayout,"展示Snackbar",Snackbar.LENGTH_SHORT).show();
是不是看着很面熟,不能说给Toast的用法差不多,那简直是一模一样啊。上个Toast的用法对比一下:
Toast.makeText(this,"展示Snackbar",Toast.LENGTH_SHORT).show();
是不是看着一模一样。
用法是一样的,但是展示的位置是不一样的,Toast默认展示位置是靠近在屏幕的底部,而Snackbar的默认展示位置是在屏幕的底部。
靠上的那个是Toast所展示的内容,下面的撑满屏幕的则是Snackbar所展示的内容。
// Snackbar.make方法会返回一个Snackbar实例,可以拿到这个实例去执行show和dismiss等方法。
// Snackbar snackbar = Snackbar.make(coordinatorLayout, "展示Snackbar", Snackbar.LENGTH_SHORT);
// snackbar.show();
Snackbar.make(coordinatorLayout,"展示Snackbar",Snackbar.LENGTH_SHORT).setAction("关闭", v1 -> {
Log.d("TAG", "点击了关闭");
// snackbar.dismiss();
}).show();
多了个关闭的按钮,这个就是我们刚刚设置的Action
这是点击之后的log信息,点击关闭之后可以手动调用Snackbar的dismiss()方法关闭Snackbar。
到这里Snackbar的简单用法和稍微进阶一点点的用法已经介绍完了。
下面来讲一下进阶用法,比如说自定义布局,自定义位置等。
其实自定义布局也是非常简单:
View rootView = this.getWindow().getDecorView();
View coordinatorLayout = rootView.findViewById(android.R.id.content);
Snackbar snackbar = Snackbar.make(coordinatorLayout, "", Snackbar.LENGTH_SHORT);
// 上面的三行代码给咱们上面说过的是一样的,这里也就不添加注释了
// 获取到Snackbar.getView获取的Snackbar的view
Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) snackbar.getView();
// 加载咱们自定义的布局
View inflate = LayoutInflater.from(snackbar.getView().getContext()).inflate(R.layout.snacbar_layout, null);
// 通过自定义布局中的控件
TextView text = inflate.findViewById(R.id.textView);
// 设置自定义的文案
text.setText("自定义布局的Snackbar");
// 通过自定义布局中的控件
ImageView imageView = inflate.findViewById(R.id.imageView);
// 设置点击事件
imageView.setOnClickListener(v1 -> Log.d("TAG", "点击了自定义布局中的控件"));
// 将获取的自定义布局view添加到SnackbarView中
snackbarView.addView(inflate);
// 展示
snackbar.show();
到这里加载自定义布局已经基本完成,但是在实践中发现了一个小坑。
这里我们自定义的布局并没有撑满到整个SnackbarView,而是在红框里面颜色比较深的就是Snackbar原来的布局背景颜色。所以这里我们需要处理一下这个情况:
View rootView = this.getWindow().getDecorView();
View coordinatorLayout = rootView.findViewById(android.R.id.content);
Snackbar snackbar = Snackbar.make(coordinatorLayout, "", Snackbar.LENGTH_SHORT);
// 上面的三行代码给咱们上面说过的是一样的,这里也就不添加注释了
// 设置SnackbarView的padding都为0,避免上图中出现黑色边框背景的情况
snackbar.getView().setPadding(0,0,0,0);
// 将SnackbarView的背景颜色设置为透明,避免在自定义布局中有圆角或者自适应宽度的时候显示一块黑色背景的情况
snackbar.getView().setBackgroundColor(Color.TRANSPARENT);
// 获取到Snackbar.getView获取的Snackbar的view
Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) snackbar.getView();
// 加载咱们自定义的布局
View inflate = LayoutInflater.from(snackbar.getView().getContext()).inflate(R.layout.snacbar_layout, null);
// 通过自定义布局中的控件
TextView text = inflate.findViewById(R.id.textView);
// 设置自定义的文案
text.setText("自定义布局的Snackbar");
// 通过自定义布局中的控件
ImageView imageView = inflate.findViewById(R.id.imageView);
// 设置点击事件
imageView.setOnClickListener(v1 -> Log.d("TAG", "点击了自定义布局中的控件"));
// 将获取的自定义布局view添加到SnackbarView中
snackbarView.addView(inflate);
// 展示
snackbar.show();
相比与上面的代码,下面修改过的代码只比上面的代码多了两行
// 设置SnackbarView的padding都为0,避免上图中出现黑色边框背景的情况
snackbar.getView().setPadding(0,0,0,0);
// 将SnackbarView的背景颜色设置为透明,避免在自定义布局中有圆角或者自适应宽度的时候显示一块黑色背景的情况
snackbar.getView().setBackgroundColor(Color.TRANSPARENT);
到这里自定义布局就基本完成了。
这里说一说自定义位置,在项目中Snackbar只显示在底部肯定是不能满足产品的变态需求的,所以就出现了这个问题。
其实这个自定义位置也是很简单
废话不多说,上代码:
View rootView = this.getWindow().getDecorView();
View coordinatorLayout = rootView.findViewById(android.R.id.content);
Snackbar snackbar = Snackbar.make(coordinatorLayout, "", Snackbar.LENGTH_SHORT);
// 上面的三行代码给咱们上面说过的是一样的,这里也就不添加注释了
// 设置SnackbarView的padding都为0,避免上图中出现黑色边框背景的情况
snackbar.getView().setPadding(0,0,0,0);
// 将SnackbarView的背景颜色设置为透明,避免在自定义布局中有圆角或者自适应宽度的时候显示一块黑色背景的情况
snackbar.getView().setBackgroundColor(Color.TRANSPARENT);
// 获取到Snackbar.getView获取的Snackbar的view
Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) snackbar.getView();
// 获取到SnackbarView的LayoutParams
ViewGroup.LayoutParams layoutParams = snackbarView.getLayoutParams();
// 新建一个LayoutParams将SnackbarView的LayoutParams的宽高传入
FrameLayout.LayoutParams fl = new FrameLayout.LayoutParams(layoutParams.width, layoutParams.height);
// 设置新的元素位置
// Gravity有许多属性,基本上可以满足大众需求, 我们这里设置了处于屏幕的中央
fl.gravity = Gravity.CENTER;
// 将新的LayoutParams设置给SnackbarView
snackbarView.setLayoutParams(fl);
// 加载咱们自定义的布局
View inflate = LayoutInflater.from(snackbar.getView().getContext()).inflate(R.layout.snacbar_layout, null);
// 通过自定义布局中的控件
TextView text = inflate.findViewById(R.id.textView);
// 设置自定义的文案
text.setText("自定义布局的Snackbar");
// 通过自定义布局中的控件
ImageView imageView = inflate.findViewById(R.id.imageView);
// 设置点击事件
imageView.setOnClickListener(v1 -> Log.d("TAG", "点击了自定义布局中的控件"));
// 将获取的自定义布局view添加到SnackbarView中
snackbarView.addView(inflate);
// 展示
snackbar.show();
相比于前一段代码也只是多了几行
// 获取到SnackbarView的LayoutParams
ViewGroup.LayoutParams layoutParams = snackbarView.getLayoutParams();
// 新建一个LayoutParams将SnackbarView的LayoutParams的宽高传入
FrameLayout.LayoutParams fl = new FrameLayout.LayoutParams(layoutParams.width, layoutParams.height);
// 设置新的元素位置
// Gravity有许多属性,基本上可以满足大众需求, 我们这里设置了处于屏幕的中央
fl.gravity = Gravity.CENTER;
// 将新的LayoutParams设置给SnackbarView
snackbarView.setLayoutParams(fl);
到这里自定义Snackbar基本就完成了。
这里还有一个小坑,就是在Android SDK 21的时候添加了一个深度属性Elevation
,如果有其他的控件设置了这个属性的话,可能会把Snackbar遮挡住。
解决方法也很简单,只需要加入一个判断就行
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
snackBar.getView().setElevation(0);
}
到这里基本上就大功告成了!
写了个Util已经上传到github,觉得有用的话可以点个star