Android Snackbar

news2024/11/16 21:43:41

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();

92ff9ab3f82449e09f7f41086249ca7a.png

注意: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()

fbef3ab2c7344652804db1b9abdcbeb8.png

 ③添加回调

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();

65eea4a867334a5bb4662aa344078bfd.webp

⑤自定义布局

自定义布局的步骤:

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();

            }

        }

    }

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1163588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝鹏测控测宽仪系列又添一员大将——双目测宽仪

轧钢过程中钢板的宽度是一个重要的参数&#xff0c;它直接决定了成材率。同时&#xff0c;随着高新科技越来越广泛的应用到工程实际中&#xff0c;许多控制系统需要钢板实时宽度值作为模型参数。 当前&#xff0c;相当一部分宽厚板厂还在采用人工检测的方法&#xff0c;检测环境…

一文5个步骤用Jmeter做接口测试!

说实话&#xff0c;在游戏测试领域&#xff0c;做接口测试的并不多&#xff0c;做的好的更是寥寥无几&#xff08;请大家不要喷游戏测试比较low&#xff0c;行业现状如此而已&#xff09;。绝大部分游戏测试人员都是以功能测试为主&#xff0c;偶尔做做性能测试和压力测试已经很…

STM32F103C8T6第二天:认识STM32 标准库与HAL库 GPIO口 推挽输出与开漏输出

1. 课程概述&#xff08;297.1&#xff09; 课程要求&#xff1a;C语言熟练&#xff0c;提前学完 C51 2. 开发软件Keil5的安装&#xff08;298.2&#xff09; 开发环境的安装 编程语言&#xff1a;C语言需要安装的软件有两个&#xff1a;Keil5 和 STM32CubeMX Keil5 的安装…

Fiddler实现 HTTP 网络抓包

文章目录 前言Fiddler 是什么下载 Fiddler1. 官网下载 Fiddler Classic2. 安装 Fiddler Classic3. 打开 Fiddler Classic 前言 前面我们简单地学习了关于应用层——自定义协议的知识&#xff0c;但是这都只是自定义协议&#xff0c;在实际生活中自定义协议用的还是占少数的&am…

终于有人把VMware虚拟机三种网络模式讲清楚了!

你们好&#xff0c;我的网工朋友。 前段时间VMware更新了&#xff0c;你用上最新版了吗&#xff1f; 有几个网工朋友留言说&#xff0c;在操作中遇到过各种各样的问题。比如说由于公司服务器重启导致出现下面的问题&#xff1a; 在Xshell里连接虚拟机映射时连接失败&#xf…

【Java|golang】2103. 环和杆---位运算

总计有 n 个环&#xff0c;环的颜色可以是红、绿、蓝中的一种。这些环分别穿在 10 根编号为 0 到 9 的杆上。 给你一个长度为 2n 的字符串 rings &#xff0c;表示这 n 个环在杆上的分布。rings 中每两个字符形成一个 颜色位置对 &#xff0c;用于描述每个环&#xff1a; 第 …

k8s之亲和性、污点

目录 亲和性 键值运算关系 硬策略 软策略 Pod亲和性与反亲和性 污点(Taint) 和 容忍(Tolerations) 污点(Taint) 容忍(Tolerations) 维护操作 故障排除步骤 亲和性 官方介绍&#xff1a;https://kubernetes.io/zh/docs/concepts/scheduling-eviction/assign-pod-nod…

nn.LayerNorm解释

这个是层归一化。我们输入一个参数&#xff0c;这个参数就必须与最后一个维度对应。但是我们也可以输入多个维度&#xff0c;但是必须从后向前对应。 import torch import torch.nn as nna torch.rand((100,5)) c nn.LayerNorm([5]) print(c(a).shape)a torch.rand((100,5,…

JMeter 接口自动化测试的最佳实践 (建议收藏)

JMeter 是一个开源的负载测试工具&#xff0c;它可以模拟多种协议和应用程序的负载&#xff0c;包括 HTTP、FTP、SMTP、JMS、SOAP 和 JDBC 等。在进行接口自动化测试时&#xff0c;使用 JMeter 可以帮助我们快速地构建测试用例&#xff0c;模拟多种场景&#xff0c;发现接口的性…

神舟十六乘组凯旋:故障预测与健康管理PHM在航空航天领域的关键作用

10月31日&#xff0c;神舟十六号载人飞船在经历五个月的太空飞行后顺利返回&#xff0c;安全着陆在内蒙古的东风着陆场&#xff0c;三位航天员安全顺利出舱。这意味着神舟十六号载人飞行任务取得圆满成功&#xff0c;标志着我国载人航天事业再创辉煌。在这背后&#xff0c;离不…

windows和docker环境下springboot整合gdal3.x

链接: gdal官网地址 gdal gdal的一个用c语言编写的库&#xff0c;用于处理地理信息相关的数据包括转换&#xff0c;识别数据&#xff0c;格式化数据以及解析 同时提供第三方语言的SDK包括python&#xff0c;java上述需要编译后使用 java是需要使用jni接口调用实现方法在wind…

力扣 搜索二维矩阵 二分

&#x1f468;‍&#x1f3eb; 搜索二维矩阵 ✨ AC code class Solution {public boolean searchMatrix(int[][] matrix, int target){int l 0;int row matrix.length;int col matrix[0].length;int r row * col - 1;while (l < r){int m l r >> 1;int x m / …

YOLOv7改进:加入解耦头Decoupled_Detect,涨点明显

💡💡💡本文全网首发独家改进:Decoupled_Detect,Hybrid Channels 策略重新设计了一个更高效的解耦头结构 Decoupled_Detect | 亲测在多个数据集能够实现涨点,多尺度特性在小目标检测表现也十分出色。 收录: YOLOv7高阶自研专栏介绍: http://t.csdnimg.cn/tYI0c…

响应式设计疑难问题全解析!一篇读懂,立即上手

在我们当前的技术环境中&#xff0c;响应式设计已经成为前端开发的重要部分。其目标是让网站能够以最优的方式在任何设备上工作——不论是大屏电脑、笔记本、平板还是智能手机。这就要求网页能够自适应不同设备的屏幕大小。下面就让我们深入浅出地探讨响应式设计的精髓&#xf…

【python海洋专题三十五】海图数据加密--二维插值

【python海洋专题三十五】海图数据加密–二维插值 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【python海洋专题三】图像修饰之画布和坐标轴 【Python海洋专题四】之水深地图图像修饰 【Python…

20231102从头开始配置cv180zb的编译环境(欢迎入坑,肯定还有很多问题等着你)

20231102从头开始配置cv180zb的编译环境&#xff08;欢迎入坑&#xff0c;肯定还有很多问题等着你&#xff09; 2023/11/2 11:31 &#xff08;欢迎入坑&#xff0c;本篇只是针对官方的文档整理的&#xff01;只装这些东西你肯定编译不过的&#xff0c;还有很多问题等着你呢&…

Linux 性能调优之资源限制(ulimitCgroup)

写在前面 考试整理相关笔记博文内容涉及 Linux 中资源限制的两种方式简单介绍 用户会话资源限制进程资源限制 理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。所有其…

【报错解决】消息号:00058

背景&#xff1a;在配置OMX3碰到了如下报错。 分析&#xff1a;根据自己目前的理解&#xff0c;valuation level 决定了valuation area.在将工厂设为valuation之后&#xff0c;工厂自动就变为了valuation area.所以起码应该配置工厂。 Tcd:OX14 解决&#xff1a;使用OX18将工…

用逻辑分析仪观察串口Uart数据波形

一、概述 只讨论嵌入式编程中较为常用的异步串行接口&#xff08;Universal Asynchronous Receiver/Transmitter&#xff0c; UART&#xff09;&#xff0c;TTL电平。 串口的参数一般有&#xff1a; 1.波特率&#xff0c;数据传输速率&#xff0c;单位bps&#xff08;bits per…

Redis Twemproxy 集群,水平扩展 ,扩容方案

文章目录 一、概述二、Twemproxy 分布模式三、测试规划四、Redis 服务实例准备4.1 配置Redis实例4.2 创建关资源4.3 启动Redis服务实例 五、Twemproxy 安装准备六、Twemproxy 安装及集群配置6.1 安装 Twemproxy6.2 配置 Twemproxy6.3 启动 twemproxy6.4 测试 twemproxy 集群 如…