Android使用协程实现自定义Toast弹框
Android使用协程实现自定义Toast弹框
最近有个消息提示需要显示10s,刚开始使用协程写了一个shoowToast方法,传入消息内容、显示时间和toast显示类型即可,以为能满足需求,结果测试说只有5s,查看日志和源码发现Android系统中Toast显示有2种类型Toast.LENGTH_SHORT和Toast.LENGTH_LONG,分别代表Toast消息显示的时间为短暂(大约2秒)和长时间(大约3.5秒),这和我们所需要的还是有很大差距的,于是通过自定义WindowManager+协程方式实现了此需求.

1.showToast方法如下:
object ToastUtils {private var toastJob :Job ?= nullfun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT, delayTime: Long = 2000L) {val toast = Toast.makeText(this@showToast, message, duration)toast.show()toastJob?.cancel()toastJob = CoroutineScope(Dispatchers.Main).launch {delay(delayTime)toast.cancel()}}
}
2.使用示例:
private fun initViews() {val textView = findViewById<TextView>(R.id.tv_test)textView.setOnClickListener {mCountdownJob = countDownCoroutines(10, lifecycleScope,onTick = { second ->textView.text = buildString {append(second)append("s后重发")}}, onStart = {// 倒计时开始}, onFinish = {// 倒计时结束,重置状态textView.text = buildString {append("发送验证码")}})showToast("祝大家国庆节快乐,万事如意",1,1000L * 10)}}
3.实现的效果如下:
可以看到虽然显示了Toast,但是5s就消失了,设置显示时间和动态传入10s都是不行的。

4.自定义Toast弹框(协程):
使用协程实现
package com.cloud.customtoastdemo.toastimport android.content.Context
import android.graphics.PixelFormat
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import com.cloud.customtoastdemo.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch/*** @auth: njb* @date: 2024/10/13 16:56* @desc: 描述*/
object EasyToast {private var easyToastView: View? = nullprivate var windowManager: WindowManager? = nullprivate var mToastJob:Job ?= nullprivate val TAG = "EasyToast"/**** @param context 上下文* @param message 提示内容消息* @param duration 可动态设置在的显示时间* @param gravity 显示位置 top、center、bottom*/fun showCustomToast(context: Context, message: String, duration: Int, gravity: Int) {windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManagerval inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflaterif (easyToastView == null) {easyToastView = inflater.inflate(R.layout.custom_easy_toast, null)val textView = easyToastView?.findViewById<TextView>(R.id.tv_message)textView?.text = message}val params = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.TYPE_APPLICATION,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSLUCENT)params.gravity = gravityparams.x = 0params.y = 0if (easyToastView?.parent == null) {windowManager?.addView(easyToastView, params)}mToastJob?.cancel()mToastJob = CoroutineScope(Dispatchers.Main).launch {delay(duration.toLong())Log.d(TAG, "时间到了结束弹框$duration")if (easyToastView != null) {windowManager?.removeView(easyToastView)easyToastView = null}}}fun cancelEasyToast() {if (easyToastView != null) {windowManager?.removeView(easyToastView)easyToastView = null}mToastJob?.cancel()}
}
5.自定义Toast弹框(Handler):
使用Handler实现:
package com.cloud.customtoastdemo.toastimport android.content.Context
import android.graphics.PixelFormat
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import com.cloud.customtoastdemo.R/*** @auth: njb* @date: 2024/10/13 16:56* @desc: 描述*/object EasyToast {private var toastView: View? = nullprivate var easyToastView: View? = nullprivate var windowManager: WindowManager? = nullprivate val handler = Handler(Looper.getMainLooper())private lateinit var runnable: Runnable/**** @param context 上下文* @param message 提示内容* @param message 提示内容消息* @param duration 显示时间* @param gravity 显示位置* @param gravity 显示位置 top、center、bottom*/fun showToast(context: Context, message: String, duration: Int, gravity: Int) {windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManagerval inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflaterif (toastView == null) {toastView = inflater.inflate(R.layout.custom_toast, null)val textView = toastView?.findViewById<TextView>(R.id.tv_message)if (easyToastView == null) {easyToastView = inflater.inflate(R.layout.custom_toast, null)val textView = easyToastView?.findViewById<TextView>(R.id.tv_message)textView?.text = message}val params = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.TYPE_APPLICATION,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSLUCENT)params.gravity = gravityparams.x = 0params.y = 0if (toastView?.parent == null) {windowManager?.addView(toastView, params)if (easyToastView?.parent == null) {windowManager?.addView(easyToastView, params)}runnable = Runnable {if (toastView != null) {windowManager?.removeView(toastView)toastView = nullif (easyToastView != null) {windowManager?.removeView(easyToastView)easyToastView = null}handler.removeCallbacks(runnable)}handler.postDelayed(runnable!!, duration.toLong())}fun cancelEasyToast() {runnable?.let {handler.removeCallbacks(it)}if (toastView != null) {windowManager?.removeView(toastView)toastView = nullif (easyToastView != null) {windowManager?.removeView(easyToastView)easyToastView = null}}}
6.使用示例:
package com.cloud.customtoastdemo
import android.os.Bundle
import android.util.Log
import android.view.Gravity
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import com.cloud.customtoastdemo.contants.Constants
import com.cloud.customtoastdemo.toast.EasyToast
import com.cloud.customtoastdemo.toast.ToastUtils.showToast
import com.cloud.customtoastdemo.utils.CountDownUtils.countDownCoroutines
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
private var mCountdownJob: Job? = null
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContentView(R.layout.activity_main)ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)insets}initViews()
}private fun initViews() {val textView = findViewById<TextView>(R.id.tv_test)textView.setOnClickListener {mCountdownJob = countDownCoroutines(10, lifecycleScope,onTick = { second ->Log.d(TAG, "toast显示时间$second")textView.text = buildString {append(second)append("s后重发")}}, onStart = {// 倒计时开始}, onFinish = {// 倒计时结束,重置状态textView.text = buildString {append("发送验证码")}})
/*
lifecycleScope.launch {
delay(1000)
showToast(“祝大家国庆节快乐,万事如意”,1,1000L * 10)
}
*/
EasyToast.showCustomToast(this@MainActivity,message = buildString {append("祝大家国庆节快乐,万事如意")},duration = Constants.TOAST_SHOW_TIME,gravity = Gravity.TOP)}
}
}
7.实现效果如下:


8.日志打印:


9.总结:
从上面的截图可以看出基本上是满足要求的,显示了10sToast提示才消失,至于这个显示时间你可以根据自己的需求动态设置,我这里也没有设置默认时长,尝试过利用反射修改Toast的显示时间和协程delpay方式设置显示时间都没有生效,所以采用WindowManager+协程的方式,当然使用Handler+dialog也可以,今天的内容就到这里,如何实现动态显示Toast时长,打卡收工,关机睡觉.
10.demo地址如下:
https://gitee.com/jackning_admin/custom-toast-demo
相关文章:
Android使用协程实现自定义Toast弹框
Android使用协程实现自定义Toast弹框 最近有个消息提示需要显示10s,刚开始使用协程写了一个shoowToast方法,传入消息内容、显示时间和toast显示类型即可,以为能满足需求,结果测试说只有5s,查看日志和源码发现Android系统中Toa…...
git diff命令详解
git diff 是 Git 中非常常用的命令,用于比较不同版本的文件改动。可以比较工作区、暂存区、或者提交之间的差异。下面是对 git diff 常用场景的详细解释: 1. git diff 当你执行 git diff 时,它会显示工作区与暂存区之间的差异,也…...
Vue 插槽:组件通信的“隐形通道”
在 Vue 中,插槽(slot)是实现组件内容分发的机制,允许我们将子组件的内容传递给父组件,从而提升组件的可复用性和灵活性。插槽的本质是通过将父组件内容传递到子组件指定的插槽位置,使得子组件在渲染时可以动…...
react1816中的setState同步还是异步的深层分析
setState 是 react 中更新 UI 的唯一方法,其内部实现原理如下: 调用 setState 函数时,React 将传入的参数对象加入到组件的更新队列中。React 会调度一次更新(reconciliation),在调度过程中,Re…...
【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第七篇-体积纹理绘制】
我们前几篇已经完成了渲染部分,现在终于开始做动态绘制功能了 之前使用的是这样一个体积雾的切片图,那么现在要做的就是动态编辑它 首先,让我们简单了解一下它是如何运作的: 开始绘制画布以渲染目标,并将材质绘制到画…...
Linux的环境搭建
目录 1、linux的简单介绍 2、搭建linux环境 2.1 linux的环境安装 2.2 使用Xshell远程登入linux 2.2.1 Xshell免密登入 2.3 windows与Xshell与linux云服务器的关系 1、linux的简单介绍 linux操作系统 为 部分汇编 C语言编写 的操作系统 源代码公开(开源),官…...
WPF+Mvvm案例实战(五)- 自定义雷达图实现
文章目录 1、项目准备1、创建文件2、用户控件库 2、功能实现1、用户控件库1、控件样式实现2、数据模型实现 2、应用程序代码实现1.UI层代码实现2、数据后台代码实现3、主界面菜单添加1、后台按钮方法改造:2、按钮添加:3、依赖注入 3、运行效果4、源代码获…...
网络爬虫-Python网络爬虫和C#网络爬虫
爬虫是一种从互联网抓取数据信息的自动化程序,通过 HTTP 协议向网站发送请求,获取网页内容,并通过分析网页内容来抓取和存储网页数据。爬虫可以在抓取过程中进行各种异常处理、错误重试等操作,确保爬取持续高效地运行 1、Python网…...
如何有效解除TikTok账号间的IP关联
在当今社交媒体环境中,TikTok凭借其独特的短视频形式吸引了数以亿计的用户。对许多内容创作者而言,运营多个账号是获取更大曝光和丰富内容的有效策略。然而,如何避免这些账号之间的IP关联,以防止被平台识别并封禁,成为…...
Python自省机制
Python 自省机制 Python 自省(Introspection)是一种动态检查对象的能力,使得开发者可以在运行时获取对象的相关信息,比如属性、方法、类型等。自省机制让 Python 具备了更强的动态性和灵活性,便于调试和开发。 自省&…...
wgan-gp 对连续变量 训练,6万条数据,训练结果不错,但是到局部的时候,拟合不好,是否可以对局部数据也进行计算呢
Wasserstein GAN with Gradient Penalty (WGAN-GP) 是一种改进的生成对抗网络(GAN),它通过引入梯度惩罚来改进训练过程,从而提高生成模型的稳定性和质量。如果你在使用WGAN-GP对连续变量进行训练时,发现整体训练结果不…...
python 制作 发货单 (生成 html, pdf)
起因, 目的: 某个小店,想做个发货单。 过程: 先写一个 html 模板。准备数据, 一般是从数据库读取,也可以是 json 格式,或是 python 字典。总之,是数据内容。使用 jinja2 来渲染模板。最终的结果可以是 h…...
GeoWebCache1.26调用ArcGIS切片
常用网址: GeoServer GeoWebCache (osgeo.org) GeoServer 用户手册 — GeoServer 2.20.x 用户手册 一、版本需要适配:Geoserver与GeoWebCache、jdk等的版本适配对照 查看来源 二、准备工作 1、数据:Arcgis标准的切片,通过…...
深度学习-卷积神经网络-基于VGG16模型, 实现猫狗二分类(文末附带数据集下载链接, 长期有效)
简介: 1.基于VGG16模型进行特征提取, 结合mlp实现猫狗二分类 2.训练数据--"dog_cat_class\training_set" 3.模型训练流程 1.对图像数据进行导入和预处理 2.搭建模型, 导入VGG16模型, 去除mlp层, 将经过VGG16训练后的数据作为输入, 输入到自建的mlp层中进行训练, 要…...
计算Java集合占用的空间【详解】
以ArrayList为例,假设集合元素类型是Person类型,假设集合容量为10,目前有两个person对象{name:“Jack”,age12} {name:“Tom”,age14} public class Person{private String name;private int age; }估算Person对象占用的大小: 对…...
仕考网:关于中级经济师考试的介绍
中级经济师考试是一种职称考试,每年举办一次,报名时间在7-8月,考试时间在10-11月 报名入口:中guo人事考试网 报名条件: 1.高中毕业并取得初级经济专业技术资格,从事相关专业工作满10年; 2.具备大学专科…...
SYN590RL 300MHz至450MHz ASK接收机芯片IC
一般描述 SYN590RL是赛诺克全新开发设计的一款宽电压范围,低功耗,高性能,无需外置AGC电容,灵敏度达到典型-110dBm,300MHz”450MHz 频率范围应用的单芯片ASK或OOK射频接收器。 SYN59ORL是一款典型的即插即用型单片高集成度无线接收器&…...
15分钟学 Go 第 20 天:Go的错误处理
第20天:Go的错误处理 目标 学习如何处理错误,以确保Go程序的健壮性和可维护性。 1. 错误处理的重要性 在开发中,错误处理至关重要。程序在运行时可能会出现各种问题,例如文件未找到、网络连接失败等。正确的错误处理能帮助我们…...
C++——string的模拟实现(上)
目录 引言 成员变量 1.基本框架 成员函数 1.构造函数和析构函数 2.拷贝构造函数 3.容量操作函数 3.1 有效长度和容量大小 3.2 容量操作 3.3 访问操作 (1)operator[]函数 (2)iterator迭代器 3.4 修改操作 (1)push_back()和append() (2)operator函数 引言 在 C—…...
JavaCV 之均值滤波:图像降噪与模糊的权衡之道
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
