SnackBar是DesignSupportLibrary中的一个重要的控件,用于在界面下面提示一些关键信息,跟Toast不同的地方是SnackBar允许用户向右滑动消除它,同时,也允许在SnackBar中设定一个Action,当用户点击了SnackBar里面的按钮的时候,可以进行一些操作,所以,功能绝对是很强大的。

SnackBar的构造:

// 参数分别是父容器,提示信息,持续时间
public
static Snackbar make(@NonNull View view, @NonNull CharSequence text,@Duration int duration)

SnackBar的常用方法:

// 用于给SnackBar设定一个Action,点击之后会回调OnclickListener中的Onclick方法
public
Snackbar setAction(CharSequence text, final View.OnClickListener listener)
// 用于设定Action的字体颜色
public
Snackbar setActionTextColor(@ColorInt int color)
// 设定提示的字体
public
Snackbar setText(@NonNull CharSequence message)
// 展示SnackBar
public
void show()
// 清除SnackBar
public
void dismiss()
// 设置回调,比如OnDismissed或者OnShown
public
Snackbar setCallback(Callback callback)

简单是使用就不说了,这里直接说一下设置Action和设置Callback

要求:实现一个撤销的操作,用户点击列表中每一项的删除按钮之后,删除(数据库中)对应的行,然后弹出SnackBar提示是否撤销,如果选择是,则恢复删除的行,否则删除该行,效果如下

Android-SnackBar

思路:

①先对我们要删除的这个行进行备份,然后在点击删除按钮的时候把行删除,把数据从源删除,然后刷新

②弹出SnackBar,设定Action,如果点了Yes,就把数据插入到源中,刷新

③设定CallBack,监听SnackBar的消失事件,如果不是点击Action而消失,则将数据从SQLite中删除

注意:这里为什么要设定监听?因为如果不进行监听,需要在删除的时候访问数据库,撤销是时候进行数据库的插入操作,这样列表中的条目位置会发生改变

我们可以在Adapter中对删除按钮进行监听,代码如下:

Android-SnackBar
 1 delete.setOnClickListener(new View.OnClickListener() {
 2             @Override
 3             public void onClick(View v) {
 4                 backupMap = mdata.get(position);
 5                 mdata.remove(position);
 6                 notifyDataSetChanged();
 7                 Snackbar snackBar = Snackbar.make(root, "是否撤销删除?", Snackbar.LENGTH_LONG);
 8                 snackBar.setAction("YES", new MyOnClickListener(position));
 9                 snackBar.setCallback(new MyCallback());
10                 snackBar.show();
11             }
12         });
Android-SnackBar

如果点击了删除按钮,我们对数据进行备份,然后删除数据源mdata对应的这个map,接下来刷新界面。然后第7行构造了一个SnackBar,询问是否撤销删除,并把持续时间设置为LONG以免用户未阅读完文字SnackBar就消失了。第8行设定了点击的Action,提示信息为“YES”,并提供了点击的监听。第9行设定了Callback,判断SnackBar的消失状态。第10行就直接把SnackBar展示出来。

下面来看Action的点击事件:

Android-SnackBar
 1 private class MyOnClickListener implements View.OnClickListener {
 2         private final int position;
 3 
 4         public MyOnClickListener(int position) {
 5             this.position = position;
 6         }
 7 
 8         @Override
 9         public void onClick(View v) {
10             mdata.add(position, backupMap);
11             notifyDataSetChanged();
12         }
13     }
Android-SnackBar

如果点击了Action,就用备份的数据进行恢复

下面看Callback:

Android-SnackBar
 1 private class MyCallback extends Snackbar.Callback {
 2         @Override
 3         public void onDismissed(Snackbar snackbar, int event) {
 4             super.onDismissed(snackbar, event);
 5             if (event == DISMISS_EVENT_SWIPE || event == DISMISS_EVENT_TIMEOUT || event ==
 6                     DISMISS_EVENT_CONSECUTIVE) {
 7                 mdatabase.execSQL("delete from tally where id=?", new String[]{backupMap.get
 8                         ("id").toString()});
 9             }
10         }
11     }
Android-SnackBar

这里看到我们重写了onDismissed方法,在SnackBar消失的时候会回调这个方法,我们先判断这个消失的类型,如果是点击了Action,就不用删除数据库中的数据,否则就对数据库进行删除。

SnackBar消失的类型对应的常量:

Android-SnackBar
/** Indicates that the Snackbar was dismissed via a swipe.*/
public static final int DISMISS_EVENT_SWIPE = 0;
/** Indicates that the Snackbar was dismissed via an action click.*/
public static final int DISMISS_EVENT_ACTION = 1;
/** Indicates that the Snackbar was dismissed via a timeout.*/
public static final int DISMISS_EVENT_TIMEOUT = 2;
/** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
public static final int DISMISS_EVENT_MANUAL = 3;
/** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
public static final int DISMISS_EVENT_CONSECUTIVE = 4;
Android-SnackBar

可以看到,从0到4分别是滑动清除、点击Action、持续时间结束、调用dismiss方法以及有新的SnackBar产生

因为每次SnackBar消失的时候都是调用了dismiss方法,所以我们只需要判断此时的状态是否为滑动清除、持续时间结束或者新的SnackBar产生,如果是,则证明用户不需要撤销,则对数据库进行删除。

最后,如果使用SnackBar,记得添加依赖!!


一、前言

Design Support Library 是 Google 发布的一个全新的兼容函数库,它可以在 Android 2.1 (Api level 7)及以上的设备中,实现 Material Design 的效果,这个函数库同时也提供了一系列控件。

今天介绍的 SnackBar 就是其中之一。

在使用 Design Support Library 之前,需要在 build.gradle 文件中,添加依赖。


  1. compile 'com.android.support:design:25.3.0' 

二、SnackBar 的常规使用

SnackBar 是一个轻量级的控件,它显示在屏幕的底部,并且在显示和隐藏的时候,带有动画效果。主要用于做一个快速的提示,它可以完美的替代 Toast ,并且在使用的 API 上,也完全和 Toast 的使用方式类似,所以掌握起来,难度并不大。

它和 Toast 最大的不同,是 SnackBar 可以带有一个按钮,也就是说它可以承载简单的交互逻辑。

先来看看 SnackBar 能实现的效果。

Android-SnackBar

可以看到,它会在底部显示一个消息条,并且在显示和隐藏的时候,都是自带动画的,可以承载一个TextView 和 Button。

SnackBar 的 API ,其实很多是参考了 Toast 的,所以他们的使用方式非常的相像。

就拿上面的例子中来看,代码也非常的简单。

Android-SnackBar

SnackBar 之需要传入一个 ViewGroup 然后它就可以在这个 ViewGroup 中显示,通常我们会将这个 ViewGroup 置于屏幕的底部。

SnackBar 是没有 public 的构造方法的,所以需要使用 make() 方法,获取到一个 SnackBar 对象,然后调用 show() 方法,即可显示出来。

在上面的例子中,还使用 setAction() 方法,为其右边的按钮设定文字以及一个点击事件。

下面介绍一下 SnackBar 自带的一些基本 api:

make():构造一个 SnackBar 对象,可进行简单配置。

show():用于显示一个已经构造好的 SnackBar。

setText():为 SnackBar 的设置提示的消息内容。

setAction():用于指定右边的按钮显示的文字以及相应的惦记事件。

setActionTextColor():设定右边按钮文字的颜色。

setCallback():设置 SnackBar 的显示和隐藏时候的回掉监听。

setDuration():更新 Duration。

可以看到,SnackBar 本身只提供了非常简单的 API 实现,看来 Google 是指望开发者完全按照他们的风格来设计 App。

三、带着问题来看 SnackBar

前面已经介绍了 SnackBar 的基本 API 的使用,如果想做其他的设置,就需要我们自己进行一些操作了。那么接下来就让我们带着问题来看如何使用 SnackBar 。

下面会涉及到一些 SnackBar 的源码,没兴趣的可以跳过直接看每个问题最后的结论即可。

1、能不能设置一个常驻的 SnackBar

从上面的例子中可以看到,SnackBar 有点模仿 Toast 的意思,给出的两个可供我们选择的值,LENGTH_SHORT 、LENGTH_LONG ,分别表示两个不同显示时间的 SnackBar。

Android-SnackBar

从代码的文档上看,貌似是没有提供给我们用以设定常驻的 SnackBar 的方式。

但是细心看看源码,可以发现,duration,是通过 @Duration 接口限定输入的,而 duration 实际上是有三个取值的,另外一个就是可以设置常驻的。

Android-SnackBar

所以,如果我们有对 SnackBar 有常驻需求的话,可以使用 LENGTH_INDEFINITE 标记即可。

2、去除掉滑动删除功能

前面介绍过,SnackBar 是需要有一个 ViewGroup 容器来容纳它的,而官方推荐使用 CoordinatorLayout 这个 ViewGroup,它实际上也是 Support Design Library 中提供的容器控件。

官方之所以推荐使用它,就是因为它可以让用户通过在 SnackBar 上进行右滑操作,进行删除。

虽然说是这么说,我们还是从源码中看看具体实现。

SnackBar 是继承自 BaseTransientBottomBar 的,而这一段实现正是在父类中。

Android-SnackBar

如图所示,如果 SnackBar 的父布局是 CoordinatorLayout 的话,就使用 Behavior 来实现滑动删除功能。

所以我们如果不需要滑动删除的功能,可以考虑用一个 FrameLayout 来容纳 SnackBar。

或者需要滑动功能,却发现没有实现,检查一下布局,看承载 SnackBar 的容器,是不是 CoordinatorLayout。

3、禁用动画能做到吗?

不知道会不会有一些交互设计师要求不要动画,就这么生硬的显示出来。那么我们来看看到底动画是不是可以被禁用掉。

执行 SnackBar 显示和隐藏动画的逻辑,依然在它的父类BaseTransientBottomBar 中。查看源码可以看到,它在执行显示和隐藏之前,都会调用 shouldAnimate() 方法,来判断是否需要执行一个动画。

Android-SnackBar

这么看,好像 SnackBar 是可以支持关闭动画的,再看看 animateView 的实现。

Android-SnackBar

是否使用动画是依赖 AccessibilityManager 中的 enable 属性决定的,而它是一个私有的属性,并且没有提供修改它的方法,并且如果用反射修改它的值,不确定会不会出现其他的问题,有待验证。

那么可以简单的认为,SnackBar 的动画,是无法简单关闭的。

4、让 SnackBar 显示在顶部可以吗?

既然 SnackBar 是有一个外部容器来承载它的,也就是说,容器在哪里,它实际上就出现在哪里。

所以如果将它置为顶部,其实是可以让它在顶部出现的。但是你以为这样就完了吗?还需要考虑动画的问题,虽然 SnackBar 会出现在顶部,但是动画依然是从下到上出现的,你就会得到一个非常诡异的 SnackBar 。

Android-SnackBar

这明显不是我们想要的。那么是不是想办法改变它出现和隐藏的动画就可以了,继续在源码内找答案。

Android-SnackBar

animateViewIn() 方法就是 SnackBar 显示时候调用的动画,但是实际上,它无法被重写。

所以,将 SnackBar 置于顶部,并且完美的执行动画的设想是达不到的。

5、修改其他的UI样式可以实现吗?

SnackBar 原本提供的可以修改 UI 样式的 API 非常的少,它只能修改右边 Button 字体的颜色。

我们继续在源码内找答案,看看源码可以发现,它的布局是在 SnackBar 中 inflater 出来的。布局文件为,design_layout_snackbar_include.xml

Android-SnackBar

SnackBar 就是用一个 TextView 和一个 Button 实现的。也就是说,我们可以直接找到这两个控件,来改变它的样式。

Android-SnackBar

参考 setText() 方法可以看到,实际上它是通过 mView 对象,拿到一个 SnackbarContentLayout 对象进行操作。mView 这个 View 就是我们需要的。SnackBar 正好也提供了它的 get 方法,所以只需要拿到它,然后对其内的 View 进行样式的修改,即可达到我们的需求。

所以,对于 SnackBar 的样式修改,只要通过 getView() 拿到 mView 对象之后,就可以实现样式的修改了。



相关文章: