这篇博客是学习完 Android 6.0 的新控件 SnackBar 后所写。如有不妥之处,欢迎留言,谢谢~

SnackBar

SnackBar 的简述

Snackbar 是 Android Support Design Library 库中的一个控件。

优势:

  • 通常在屏幕底部快速弹出消息(位置取决于容纳 SnackBar 控件的位置)
  • 比 Toast 更加好用
  • 比 Dialog 更轻量级
  • 可与用户进行简单的交互
  • 可滑动删除

SnackBar 的基本使用

在使用 SnackBar 之前,需要导入 com.android.support:design 库:

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

PS:需要一个控件容器来容纳 SnackBar 。Google 推荐使用 CoordinatorLayout 来作为 SnackBar 的容器。在 SnackBar 的源码中是这样解释的:

CoordinatorLayout 也是 Android Support Design Library 库中的一个控件。CoordinatorLayout 使用新的思路通过协调调度子布局的形式实现触摸影响布局的形式产生动画效果。其有两个功能: 作为顶层布局;调度协调子布局。

  • 最简单的 SnackBar 使用方式与 Toast 十分相似。

    • 图例:

    • 使用方式:
      1
      2
      3
      4
      5
      6
      7
      /**
      * 用于创建SnackBar
      * @param view 容纳SnackBar的父容器
      * @param text 显示在左边的Message
      * @param duration 持续时间:Snackbar.LENGTH_SHORT或LENGTH_LONG
      */
      Snackbar.make(View view,CharSequence text,int duration).show(); // 显示
  • 右侧还有按钮的 SnackBar

    • 图例:

    • 使用方式:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      Snackbar.make(View view,CharSequence text,int duration)
      /**
      * 设置 SnackBar 右侧的按钮
      * @param text 按钮名字
      * @param listener 点击监听事件
      */
      .setAction(CharSequence text,new View.OnClickListener(){
      @Override
      public void onClick(View v) {
      // 点击 SnackBar 右侧按钮后的逻辑
      }
      })
      .show(); // 显示
  • 若想在 SnackBar 出现或消失时,做其他逻辑,可以使用其Callback方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Snackbar.make(View view,CharSequence text,int duration)
    .setCallback(new Snackbar.Callback() {
    @Override
    public void onDismissed(Snackbar snackbar, int event) {
    super.onDismissed(snackbar, event);
    // 消失时
    }
    @Override
    public void onShown(Snackbar snackbar) {
    super.onShown(snackbar);
    // 出现时
    }
    })
    .show(); // 显示

SnackBar 的颜色改造

在使用 SnackBar 过程中,会发现 SnackBar 和 Toast 都是样式过于单一。然而开发过程中,对不同的信息往往需要使用不同的颜色进行区分,引起用户的注意。Android 只提供了修改 Action 文字颜色的方法setActionTextColor() ,显然是不够的。那么只能我们自己阅读源码后,自行添加了。
阅读源码后,有所发现:

  • SnackBar 中定义了一个内部类 SnackbarLayout ,且这个内部类是继承 LinearLayout 的;

  • SnackbarLayout 中加载了 R.layout.design_layout_snackbar_include,那意味着 SnackBar 的布局时是有 SnackbarLayout 决定的。
    R.layout.design_layout_snackbar_include 布局文件的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView
    android:id="@+id/snackbar_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:paddingTop="14dp"
    android:paddingBottom="14dp"
    android:paddingLeft="12dp"
    android:paddingRight="12dp"
    android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
    android:maxLines="1"
    android:layout_gravity="center_vertical|left|start"
    android:ellipsize="end"
    android:textAlignment="viewStart"/>
    <Button
    android:id="@+id/snackbar_action"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="0dp"
    android:layout_marginStart="0dp"
    android:layout_gravity="center_vertical|right|end"
    android:paddingTop="14dp"
    android:paddingBottom="14dp"
    android:paddingLeft="12dp"
    android:paddingRight="12dp"
    android:visibility="gone" // 注意到没? 当设置了SnackBar有按钮时才会显示
    android:textColor="?attr/colorAccent"
    style="?attr/borderlessButtonStyle"/>
    </merge>

了解到这个后,设置消息和 SnackBar 的背景颜色就是很简单了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 设置 SnackBar 的颜色
* 因为 Action 已有设置的方法,所以这里只需设置消息和背景的颜色
*
* @param snackbar SnackBar实例
* @param messageColor 消息的颜色
* @param backgroundColor 背景颜色
*/
public static void setSnackbarColor(Snackbar snackbar, int messageColor,
int backgroundColor) {
/** 获取Snackbar的view */
View view = snackbar.getView();
if (view != null) {
// 修改view的背景色
view.setBackgroundColor(backgroundColor);
// 获取Snackbar的message控件,修改字体颜色
((TextView)view.findViewById(R.id.snackbar_text))
.setTextColor(messageColor);
}
}

SnackBar 的布局改造

Google 在 Material Design 设计规范中提到:当有2个或者2个以上的操作出现时,应该使用提示框而不是 Snackbar,即使其中的一个是取消操作。如果 Snackbar 中提示的操作重要到需要打断屏幕上正在进行的操作,那么理当使用提示框而非 Snackbar。

  但有时候我们接到的需求是要在 SnackBar 添加其他布局,怎么办呢?(我很想遵循官方的设计规范,但却因生活压力被迫接受那些奇葩的需求)

  这个时候,我们应该想到刚刚提到的,SnackBar 的布局是由 SnackbarLayout 决定的。那我们可以直接新建一个布局,然后添加进去就可以了嘛。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 为SnackBar添加布局
*
* @param snackbar SnackBar实例
* @param layoutId 布局文件
* @param index 位置
*/
public static void addViewToSnackbar(Snackbar snackbar, int layoutId, int index) {
/** 获取snackbar的View(其实就是SnackbarLayout) */
View snackbarview = snackbar.getView();
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbarview;
View add_view = LayoutInflater.from(snackbarview.getContext())
                   .inflate(layoutId, null); //加载布局文件新建View
// 设置新建布局参数
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(
                       LinearLayout.LayoutParams.WRAP_CONTENT,
                       LinearLayout.LayoutParams.WRAP_CONTENT);
// 设置新建布局在Snackbar内垂直居中显示
p.gravity = Gravity.CENTER_VERTICAL;
// 将新建布局添加进snackbarLayout相应位置
snackbarLayout.addView(add_view, index, p);
}

使用示例:
新建一个布局文件:(直接一个ImageView,并设置为默认图标)

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@mipmap/ic_launcher"/>
</LinearLayout>

Java文件中运用:

1
2
3
4
Snackbar snackbar = Snackbar.make(mContainer, "这是普通 SnackBar",Snackbar.LENGTH_SHORT);
setSnackbarColor(snackbar, Color.RED, Color.BLUE);
addViewToSnackbar(snackbar, R.layout.snackbar_image, 0);
snackbar.show();

运行后示例:

SnackBar 封装

为了更方便、灵活的使用 SnackBar,对 SnackBar 进行封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import android.graphics.Color;
import android.support.design.widget.Snackbar;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* @创建者     cnLGMing
* @创建时间    2016-6-3 16:35
* @描述      对SnackBar进行封装
*/
public class SnackBarUtils {
public static final int INFO = 1;
public static final int CONFIRM = 2;
public static final int WARNING = 3;
public static final int ALERT = 4;
public static int red = 0xfff44336;
public static int green = 0xff4caf50;
public static int blue = 0xff2195f3;
public static int orange = 0xffffc107;
/**
* Snackbar:自定义颜色的短显示
*
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
public static Snackbar shortSnackbar(View view, String message,
int messageColor,int backgroundColor) {
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT);
setSnackBarColor(snackbar, messageColor, backgroundColor);
return snackbar;
}
/**
* Snackbar:自定义颜色的长显示
*
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
public static Snackbar longSnackbar(View view, String message, int messageColor,
int backgroundColor) {
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG);
setSnackBarColor(snackbar, messageColor, backgroundColor);
return snackbar;
}
/**
* 短显示Snackbar,可选预设类型
*
* @param view
* @param message
* @param type
* @return
*/
public static Snackbar shortSnackbar(View view, String message, int type) {
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT);
switchType(snackbar, type);
return snackbar;
}
/**
* 长显示Snackbar,可选预设类型
*
* @param view
* @param message
* @param type
* @return
*/
public static Snackbar longSnackbar(View view, String message, int type) {
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG);
switchType(snackbar, type);
return snackbar;
}
/**
* 自定义时常显示Snackbar,自定义颜色
*
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
public static Snackbar IndefiniteSnackbar(View view, String message, int duration,
int messageColor, int backgroundColor) {
Snackbar snackbar = Snackbar
.make(view, message, Snackbar.LENGTH_INDEFINITE)
.setDuration(duration);
setSnackBarColor(snackbar, messageColor, backgroundColor);
return snackbar;
}
/**
* 自定义时常显示Snackbar,可选预设类型
*
* @param view
* @param message
* @param type
* @return
*/
public static Snackbar IndefiniteSnackbar(View view, String message, int duration,
int type) {
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE)
.setDuration(duration);
switchType(snackbar, type);
return snackbar;
}
/**
* 设置Snackbar背景颜色
*
* @param snackbar
* @param backgroundColor
*/
public static void setSnackBarColor(Snackbar snackbar, int backgroundColor) {
View view = snackbar.getView();
if (view != null) {
view.setBackgroundColor(backgroundColor);
}
}
/**
* 设置SnackBar消息的颜色
*
* @param snackbar
* @param messageColor
*/
public static void setSnackBarMsgColor(Snackbar snackbar, int messageColor) {
View view = snackbar.getView();
if (view != null) {
((TextView) view.findViewById(R.id.snackbar_text)).setTextColor(messageColor);
}
}
/**
* 设置Snackbar文字和背景颜色
*
* @param snackbar
* @param messageColor
* @param backgroundColor
*/
public static void setSnackBarColor(Snackbar snackbar, int messageColor,
int backgroundColor) {
View view = snackbar.getView();
if (view != null) {
view.setBackgroundColor(backgroundColor);
((TextView) view.findViewById(R.id.snackbar_text)).setTextColor(messageColor);
}
}
/**
* 为SnackBar添加布局
*
* @param snackbar SnackBar实例
* @param layoutId 布局文件
* @param index 位置
*/
public static void addView(Snackbar snackbar, int layoutId, int index) {
/** 获取snackbar的View(其实就是SnackbarLayout) */
View snackbarview = snackbar.getView();
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbarview;
View add_view = LayoutInflater.from(snackbarview.getContext())
.inflate(layoutId, null); //加载布局文件新建View
// 设置新建布局参数
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
// 设置新建布局在Snackbar内垂直居中显示
p.gravity = Gravity.CENTER_VERTICAL;
// 将新建布局添加进snackbarLayout相应位置
snackbarLayout.addView(add_view, index, p);
}
//选择预设类型
private static void switchType(Snackbar snackbar, int type) {
switch (type) {
case INFO:
setSnackBarColor(snackbar, blue);
break;
case CONFIRM:
setSnackBarColor(snackbar, green);
break;
case WARNING:
setSnackBarColor(snackbar, orange);
break;
case ALERT:
setSnackBarColor(snackbar, Color.YELLOW, red);
break;
}
}
}

项目地址

Github 传送门

SnackBar 的使用教程到此结束。

参考博文:
没时间解释了,快使用Snackbar

作者简介

刘广明(@cnLGMing),一步一步往上爬。

个人博客GitHub知乎

文章若有不对之处,欢迎指正,谢谢~

版权声明:原创作品,转载时请务必注明原始出处。

留言