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理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所…...

【Linux】开发工具——vim多模式编辑器的入土设置sudoers白名单
个人主页点击直达:小白不是程序媛 Linux系列专栏:Linux被操作记 目录 前言: 基本概念 vim基本操作 [正常模式]切换至[插入模式] [插入模式]切换至[正常模式] [正常模式]切换至[末行模式] 三种模式的切换关系图 vim命令模式命令集 进…...

Linux基础环境开发工具的使用(yum,vim,gcc,g++)
Linux基础环境开发工具的使用[yum,vim,gcc,g] 一.yum1.yum的快速入门1.yum安装软件2.yum卸载软件 2.yum的生态环境1.操作系统的分化2.四个问题1.服务器是谁提供的呢?2.服务器上的软件是谁提供的呢?3.为什么要提供呢?4.yum是如何得知目标服务器的地址和下载链接呢?5.软件源 …...

加速软件开发和交付的革命性方法-DevOps
“ 随着信息技术的快速发展,现代软件开发和交付已经经历了巨大的变革。DevOps(Development和Operations的结合)已经成为这一变革的关键推动力,让开发团队和运维团队之间的界限变得模糊,以加速软件的开发、测试和部署过…...
Ha-NeRF源码解读 train_mask_grid_sample
目录 背景: (1)Ha_NeRF论文解读 (2)Ha_NeRF源码复现 (3)train_mask_grid_sample.py 运行 train_mask_grid_sample.py解读 1 NeRFSystem 模块 2 forward()详解 3 模型训练tranining_st…...

大数据毕业设计选题推荐-系统运行情况监控系统-Hadoop-Spark-Hive
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...

SpringBoot----自定义Start(自定义依赖)
一,为什么要定义Start 向阿里云OSS如果我们要引入的话很麻烦,所以我们可以自定义一些组件, 然后我们只需要在pom文件中引入对应的坐标就可以 二,怎么定义(以阿里云OSS为例) 1, 定义两个组件模块…...

通过条件竞争实现内核提权
条件竞争漏洞(Race Condition Vulnerability)是一种在多线程或多进程并发执行时可能导致不正确行为或数据损坏的安全问题。这种漏洞通常发生在多个线程或进程试图访问和修改共享资源(如内存、文件、网络连接等)时,由于…...
vue实现换肤功能
1、使用scss定义几种需要进行换肤的颜色,例如: .font-color-theme{[color-theme"black"] & {color: #000}[color-theme"white"] & {color: #fff} }2、使用以下代码控制变化; let colorType localStorage.getIt…...
嵌入式软件工程师面试题——2025校招社招通用(八)
说明: 面试题来源于网络书籍,公司题目以及博主原创或修改(题目大部分来源于各种公司);文中很多题目,或许大家直接编译器写完,1分钟就出结果了。但在这里博主希望每一个题目,大家都要…...
背包笔记
01背包 朴素版01背包 cin >> n >> m; f[0][0] 0; for(int i 1; i < n; i ) {for(int j 0; j < m; j ){f[i][j] f[i - 1][j];//第i个物品不选if(j - v[i] > 0){f[i][j] max(f[i][j], f[i - 1][j - v[i]] w[i]);//选第i个物品}} }cout << f[n…...