Android Snackbar
1.Snackbar
Snackbar是Material Design中的一个控件,用来代替Toast。Snackbar是一个类似Toast的快速弹出消息提示的控件。Snackbar在显示上比Toast丰富,而且提供了用户交互的接口。
①默认情况下,Snackbar显示在屏幕底部,它出现在屏幕所有元素之上,且同时最多只能显示一个Snackbar。
②Snackbar与某些视图相关联,并且仅当视图在屏幕上时才会显示Snackbar。
③Snackbar出现时不会阻碍用户在屏幕上的输入。Snackbar可以自定义时长。
④当Snackbar在CoordinatorLayout下使用时,支持右滑删除功能。
2.Snackbar的用法
Snackbar的用法很简单,不需要在xml中写布局,像Toast一样直接在代码里使用即可。
首先需要添加依赖:
implementation "com.google.android.material:$latest_version"
然后就可以在代码中使用了:
①最基本的用法
Snackbar.make(view, "Show some message here",Snackbar.LENGTH_LONG)
.setAction("Action", v1 -> {
Log.e(TAG, "点击了确定按钮");
}).show();

注意:Snackbar不支持设置多个action,如果设置多个action,只有最后一个生效。
②设置颜色
Snackbar.make(view, "Show some message here", Snackbar.LENGTH_SHORT)
.setBackgroundTint(ContextCompat.getColor(this, R.color.baseCyan))
.setActionTextColor(ContextCompat.getColor(this, R.color.white))
.setTextColor(ContextCompat.getColor(this,R.color.black))
.setAction("Action") {
Log.e(TAG, "点击了确定按钮");
}
.show()

③添加回调
addCallback()用于给snackbar添加回调,回调Snackbar弹出和关闭动作。
Snackbar.make(view, "Show some message here", Snackbar.LENGTH_SHORT)
.addCallback(new Snackbar.Callback() {
public void onShown(Snackbar sb) {
super.onShown(sb)
Log.d(TAG, "onShown")
}
public void onDismissed( transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
Log.d(TAG, "onDismissed")
}
}).show();
④在文本前面添加图片
Snackbar snackbar = Snackbar.make(view, "这是一个snackbar", Snackbar.LENGTH_SHORT);
snackbar.setAction("取消", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
TextView textView = snackbar.getView().findV iewById(R.id.snackbar_text);
Drawable drawable = getResources().getDrawa ble(R.mipmap.ic_launcher_round);
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
textView.setCompoundDrawables(drawable, null, null, null);
//增加文字和图标的距离
textView.setCompoundDrawablePadding(20);
textView.setGravity(Gravity.CENTER);
snackbar.show();

⑤自定义布局
自定义布局的步骤:
1)通过Snackbar.getView获取到view;
1)通过LayoutInflater去加载布局得到自定义的布局view;
3)通过①中获取到的view添加②中加载好的布局view;
4)通过①中得到的自定义布局获取里面的控件去执行一些操作,比如点击事件,设置文字和文字颜色等。
View rootView = 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();
⑥修改Snackbar的位置
自定义位置的步骤:
1)获取到SnackbarView的LayoutParams;
2)通过①中获取到的LayoutParams创建新的LayoutParams;
③给②中的LayoutParams设置Gravity;
④将新的LayoutParams设置给SnackbarView。
View rootView = 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);
// 设置新的元素位置
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", "点击了自定义布局中的控件"));
snackbarView.addView(inflate);
snackbar.show();
3.Snackbar源码
①Snackbar使用静态方法make()创建实例
public static Snackbar make(View view, int resId, int duration) {
return make(view, view.getResources().getText(resId), duration);
}
public static Snackbar make(View view, 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.");
}
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final SnackbarContentLayout content =(SnackbarContentLayout) inflater.inflate( R.layout.design_layout_snackbar_include, parent, false);
final Snackbar snackbar = new Snackbar( parent, content, content);
snackbar.setText(text);
snackbar.setDuration(duration);
return snackbar;
}
②findSuitableParent()
创建Snackbar实例,需要寻找合适的父视图,优先选择CoordinatorLayout作为父视图。
private static ViewGroup findSuitableParent( View view) {
ViewGroup fallback = null;
do {
if (view instanceof CoordinatorLayout) {
return (ViewGroup) view;
} else if (view instanceof FrameLayout) {
if(view.getId() == android.R.id.content) {
return (ViewGroup) view;
} else {
fallback = (ViewGroup) view;
}
}
if (view != null) {
final ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
}
} while (view != null);
return fallback;
}
③SnackbarContentLayout
SnackbarContentLayout继承LinearLayout,并实现了BaseTransientBottomBar.ContentViewC allback,包含一个TextView和Button。
design_layout_snackbar_include.xml文件:
<view xmlns:android="http://schemas.andro id.com/apk/res/android"
class="android.support.design.internal.Sna ckbarContentLayout"
android:theme="@style/ThemeOverlay.Ap pCompat.Dark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom">
<TextView
android:id="@+id/snackbar_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="@dimen/design_sn ackbar_padding_vertical"
android:paddingBottom="@dimen/desig n_snackbar_padding_vertical"
android:paddingLeft="@dimen/design_sn ackbar_padding_horizontal"
android:paddingRight="@dimen/design_s nackbar_padding_horizontal"
android:textAppearance="@style/TextAp pearance.Design.Snackbar.Message"
android:maxLines="@integer/design_sna ckbar_text_max_lines"
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="@dimen/de sign_snackbar_extra_spacing_horizontal"
android:layout_marginStart="@dimen/de sign_snackbar_extra_spacing_horizontal"
android:layout_gravity="center_vertical| right|end"
android:minWidth="48dp"
android:visibility="gone"
android:textColor="?attr/colorAccent"
style="?attr/borderlessButtonStyle"/>
</view>
④SnackbarManager类
SnackbarManager用来管理Snackbar控件的状态。
Snackbar的show()方法会调用SnackbarManager的show(int, Callback)方法,而mManagerCallback会回调Snackbar的showView()和hideView(int)方法。
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;
}
});
}
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));
}
};
public void show() {
SnackbarManager.getInstance().show( mDuration, mManagerCallback);
}
SnackbarManager内部包含两个记录mCurrentSnackbar和mNextSnackbar。在SnackbarManager的show(int, Callback)方法中,①查看是否是当前Snackbar,如果是,更新超时时间,结束。②查看是否是NextSnackbar,如果是,更新数据,如果不是创建新的NextSnackbar。③取消当前Snackbar或者显示NextSnackbar。
⑤show(int, Callback)方法
public void show(int duration, Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// 如果是当前Snackbar,更新duration和超时提示
mCurrentSnackbar.duration = duration;
mHandler.removeCallbacksAndMessag es( mCurrentSnackbar);
scheduleTimeoutLocked( mCurrentSnackbar);
return;
} else if (isNextSnackbarLocked(callback)){
// 如果是NextSnackbar,更新duration
mNextSnackbar.duration = duration;
} else {
// 否则就创建新的NextSnackbar
mNextSnackbar = new SnackbarRecord(duration, callback);
}
if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar, Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
// 如果当前Snackbar存在,取消显示当前Snackbar
return;
} else {
mCurrentSnackbar = null;
// 如果当前Snackbar不存在,显示NextSnackbar
showNextSnackbarLocked();
}
}
}
// 取消显示当前Snackbar,调用callback的dismiss(DISMISS_EVENT_CONSECUTIVE)方法
private boolean cancelSnackbarLocked( SnackbarRecord record, int event) {
final Callback callback = record.callback.get();
if (callback != null) {
mHandler.removeCallbacksAndMessages( record);
callback.dismiss(event);
return true;
}
return false;
}
private boolean isCurrentSnackbarLocked( Callback callback) {
return mCurrentSnackbar != null && mCurrentSnackbar.isSnackbar(callback);
}
private boolean isNextSnackbarLocked( Callback callback) {
return mNextSnackbar != null && mNextSnackbar.isSnackbar(callback);
}
// 更新超时提示
private void scheduleTimeoutLocked( SnackbarRecord r) {
if (r.duration == Snackbar.LENGTH_INDEFINITE) {
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);
}
// 显示NextSnackbar,调用callback的show方法
private void showNextSnackbarLocked() {
if (mNextSnackbar != null) {
mCurrentSnackbar = mNextSnackbar;
mNextSnackbar = null;
final Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
} else {
mCurrentSnackbar = null;
}
}
}
Snackbar的showView()会调用onViewShown(),hideView(int)会调用onViewHidden(int):
final void showView() {
... ...
if (shouldAnimate()) {
// If animations are enabled, animate it in
animateViewIn();
} else {
// Else if anims are disabled just call back now
onViewShown();
}
... ...
}
final void hideView(final int event) {
if (shouldAnimate() && mView.getVisibility() == View.VISIBLE) {
animateViewOut(event);
} else {
// If anims are disabled or the view isn't visible, just call back now
onViewHidden(event);
}
}
void onViewShown() {
SnackbarManager.getInstance().onShown( mManagerCallback);
}
void onViewHidden(int event) {
SnackbarManager.getInstance().onDismi ssed(mManagerCallback);
}
SnackbarManager的onShown(Callback)和onDismissed(Callback)方法:
public void onShown(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
scheduleTimeoutLocked( mCurrentSnackbar);
}
}
}
public void onDismissed(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// If the callback is from a Snackbar currently show, remove it and show a new one
mCurrentSnackbar = null;
if (mNextSnackbar != null) {
showNextSnackbarLocked();
}
}
}
}
相关文章:
Android Snackbar
1.Snackbar Snackbar是Material Design中的一个控件,用来代替Toast。Snackbar是一个类似Toast的快速弹出消息提示的控件。Snackbar在显示上比Toast丰富,而且提供了用户交互的接口。 ①默认情况下,Snackbar显示在屏幕底部,它出现…...
详解API接口如何安全的传输数据(内附商品详情API接口接入方式)
概述 API接口的安全传输是确保数据在API请求和响应之间的传输过程中不被截获、篡改或泄露的重要步骤。以下是一些用于增强API接口安全传输的常见技术和最佳实践: 使用HTTPS:使用HTTPS协议而不是HTTP,以确保数据在传输过程中的安全性。HTTPS使…...
网工内推 | 大专以上,福利待遇好,IE认证优先(云厂商)
01 主动脉科技有限公司 招聘岗位:网络工程师 职责描述: 1.负责云计算,IDC,BGP网络,通过团队协作,构建云业务后台技术支持服务体系。 2.通过工单、其他通讯工具等线上方式完成对客户的实施售后支持&#x…...
Python time strptime()和strftime()
1 strptime()方法 根据指定的格式把一个时间字符串解析为时间元组 重要的时间日期格式化符号 %y 两位数的年份表示(00-99) %Y 四位数的年份表示(000-9999) %m 月份(01-12) %d 月内中的一天(0-…...
是谁家班主任还不知道 怎么发布期中成绩啊。
你知道吗?居然还有班主任不知道怎么发布期中成绩! 发布成绩并不是一件难事,只需几个步骤,就能轻松搞定! 给大家讲一下成绩查询是什么。成绩查询是指学生通过一定的方式,如输入学号、姓名等,在指…...
损失函数(Loss Function)一文详解-聚类问题常见损失函数Python代码实现+计算原理解析
损失函数(Loss Function)一文详解-聚类问题常见损失函数Python代码实现计算原理解析 前言 损失函数无疑是机器学习和深度学习效果验证的核心检验功能,用于评估模型预测值与实际值之间的差异。我们学习机器学习和深度学习或多或少都接触到了损失函数,但…...
测试用例设计方法 —— 场景法详解
场景法是通过运用场景来对系统的功能点或业务流程的描述,从而提高测试效果的一种方法。 场景法一般包含基本流和备用流,从一个流程开始,通过描述经过的路径来确定的过程,经过遍历所有的基本流和备用流来完成整个场景。 场景主要…...
el-table表格设置——动态修改表头
(1) 首先是form表单写表单设置按钮: (1.1)使用el-popover,你需要修改的是this.colOptions,colSelect: <el-popover id"popover" popper-class"planProver" placement"bottom" width&…...
京东数据分析:2023年9月京东洗地机行业品牌销售排行榜
鲸参谋监测的京东平台9月份洗地机市场销售数据已出炉! 9月份,洗地机市场的销售额增长。根据鲸参谋电商数据分析平台的相关数据显示,9月京东平台上洗地机的销量为9.2万,销售额将近2.2亿,同比增长约9%。从价格上看&#…...
使用 TensorFlow SSD 网络进行对象检测
使用 TensorFlow SSD 网络进行对象检测 目录 描述这个示例是如何工作的? 处理输入图准备数据sampleUffSSD 插件验证输出TensorRT API 层和操作 先决条件运行示例 示例 --help 选项 附加资源许可证更改日志已知问题 描述 该示例 sampleUffSSD 预处理 TensorFlow …...
(2)STM32单片机上位机
使用VX小程序开发上位机, 样式如何创建? 在你所在页面 开辟空间 使用 view 在view 中 输入class 就是样式,在编辑样式的时候,如何寻找哪一块的样式 就是通过这个class寻找的 按钮使用switch...
从InnoDB索引的数据结构,去理解索引
从InnoDB索引的数据结构,去理解索引 1、InnoDB 中的 BTree1.1、BTree 的组成1.2、BTree中的数据页 2、聚簇索引2.1、聚簇索引的特点2.2、聚簇索引的结构示例2.3、聚簇索引的优缺点 3、非聚簇索引3.1、非聚簇索引结构示例3.2、关于回表3.3、聚簇索引和非聚簇索引的区…...
Nacos:动态服务发现与配置管理的终极解决方案
今天我想和大家分享一下Nacos,这是一个由阿里巴巴开源的动态服务发现、配置和服务管理平台。我将详细介绍Nacos的主要特性,并通过实例来演示如何使用它。同时,我还会指出Nacos的优点,希望这篇文章能够帮助大家更好地理解和使用Nac…...
易思无人值守智能物流系统Sys_ReportFile文件上传漏洞复现
文章目录 易思无人值守智能物流系统Sys_ReportFile文件上传漏洞复现0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 易思无人值守智能物流系统Sys_ReportFile文件上传漏洞复现 0x01 前言 免责声明:请…...
java Map List转化,通过Map保存数据,通过List排序。取前三名
java Map List转化,通过Map保存数据,通过List排序。取前三名 package yo;import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map;public class a {public static void …...
LEECODE 1480一维数组的动态和
class Solution { public:vector<int> runningSum(vector<int>& nums) {vector<int> runningSum;int sum 0;int len nums.size();for(int i 0; i < len; i){sum sum nums[i];runningSum.push_back(sum);}return runningSum;} };...
python文档链接
python与并行计算...
HTTP调试代理工具/Proxyman
Proxyman专为开发人员和网络爱好者设计,它允许用户拦截、查看和修改所有传入和传出的网络请求,并提供详细的分析和调试功能。 Proxyman支持HTTP、HTTPS和WebSocket协议,因此,可以轻松捕获和查看这些协议下的网络流量。用户可以使…...
搭建Qt5.7.1+kylinV10开发环境、运行环境
1.下载Qt源码 Index of / 2.编译Qt 解压缩qt-everywhere-opensource-src-5.7.1.tar.gz 进入到qt-everywhere-opensource-src-5.7.1/qtbase/mkspecs这个目录下, 2.1找到以下目录 复制他,然后改名linux-x86-arrch64,博主这里名字取的有些问…...
Ceph:关于Ceph 中创建和管理自定义 CRUSH Map
写在前面 准备考试,整理 Ceph 相关笔记博文内容涉及,管理和定制CRUSH Map以及管理OSD Map理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...
