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

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...

Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...