当前位置: 首页 > news >正文

Android使用协程实现自定义Toast弹框

Android使用协程实现自定义Toast弹框

​ 最近有个消息提示需要显示10s,刚开始使用协程写了一个shoowToast方法,传入消息内容、显示时间和toast显示类型即可,以为能满足需求,结果测试说只有5s,查看日志和源码发现Android系统中Toast显示有2种类型Toast.LENGTH_SHORTToast.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方法&#xff0c;传入消息内容、显示时间和toast显示类型即可&#xff0c;以为能满足需求&#xff0c;结果测试说只有5s&#xff0c;查看日志和源码发现Android系统中Toa…...

git diff命令详解

git diff 是 Git 中非常常用的命令&#xff0c;用于比较不同版本的文件改动。可以比较工作区、暂存区、或者提交之间的差异。下面是对 git diff 常用场景的详细解释&#xff1a; 1. git diff 当你执行 git diff 时&#xff0c;它会显示工作区与暂存区之间的差异&#xff0c;也…...

Vue 插槽:组件通信的“隐形通道”

在 Vue 中&#xff0c;插槽&#xff08;slot&#xff09;是实现组件内容分发的机制&#xff0c;允许我们将子组件的内容传递给父组件&#xff0c;从而提升组件的可复用性和灵活性。插槽的本质是通过将父组件内容传递到子组件指定的插槽位置&#xff0c;使得子组件在渲染时可以动…...

react1816中的setState同步还是异步的深层分析

setState 是 react 中更新 UI 的唯一方法&#xff0c;其内部实现原理如下&#xff1a; 调用 setState 函数时&#xff0c;React 将传入的参数对象加入到组件的更新队列中。React 会调度一次更新&#xff08;reconciliation&#xff09;&#xff0c;在调度过程中&#xff0c;Re…...

【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第七篇-体积纹理绘制】

我们前几篇已经完成了渲染部分&#xff0c;现在终于开始做动态绘制功能了 之前使用的是这样一个体积雾的切片图&#xff0c;那么现在要做的就是动态编辑它 首先&#xff0c;让我们简单了解一下它是如何运作的&#xff1a; 开始绘制画布以渲染目标&#xff0c;并将材质绘制到画…...

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语言编写 的操作系统 源代码公开(开源)&#xff0c;官…...

WPF+Mvvm案例实战(五)- 自定义雷达图实现

文章目录 1、项目准备1、创建文件2、用户控件库 2、功能实现1、用户控件库1、控件样式实现2、数据模型实现 2、应用程序代码实现1.UI层代码实现2、数据后台代码实现3、主界面菜单添加1、后台按钮方法改造&#xff1a;2、按钮添加&#xff1a;3、依赖注入 3、运行效果4、源代码获…...

网络爬虫-Python网络爬虫和C#网络爬虫

爬虫是一种从互联网抓取数据信息的自动化程序&#xff0c;通过 HTTP 协议向网站发送请求&#xff0c;获取网页内容&#xff0c;并通过分析网页内容来抓取和存储网页数据。爬虫可以在抓取过程中进行各种异常处理、错误重试等操作&#xff0c;确保爬取持续高效地运行 1、Python网…...

如何有效解除TikTok账号间的IP关联

在当今社交媒体环境中&#xff0c;TikTok凭借其独特的短视频形式吸引了数以亿计的用户。对许多内容创作者而言&#xff0c;运营多个账号是获取更大曝光和丰富内容的有效策略。然而&#xff0c;如何避免这些账号之间的IP关联&#xff0c;以防止被平台识别并封禁&#xff0c;成为…...

Python自省机制

Python 自省机制 Python 自省&#xff08;Introspection&#xff09;是一种动态检查对象的能力&#xff0c;使得开发者可以在运行时获取对象的相关信息&#xff0c;比如属性、方法、类型等。自省机制让 Python 具备了更强的动态性和灵活性&#xff0c;便于调试和开发。 自省&…...

wgan-gp 对连续变量 训练,6万条数据,训练结果不错,但是到局部的时候,拟合不好,是否可以对局部数据也进行计算呢

Wasserstein GAN with Gradient Penalty (WGAN-GP) 是一种改进的生成对抗网络&#xff08;GAN&#xff09;&#xff0c;它通过引入梯度惩罚来改进训练过程&#xff0c;从而提高生成模型的稳定性和质量。如果你在使用WGAN-GP对连续变量进行训练时&#xff0c;发现整体训练结果不…...

python 制作 发货单 (生成 html, pdf)

起因&#xff0c; 目的: 某个小店&#xff0c;想做个发货单。 过程: 先写一个 html 模板。准备数据&#xff0c; 一般是从数据库读取&#xff0c;也可以是 json 格式&#xff0c;或是 python 字典。总之&#xff0c;是数据内容。使用 jinja2 来渲染模板。最终的结果可以是 h…...

GeoWebCache1.26调用ArcGIS切片

常用网址&#xff1a; GeoServer GeoWebCache (osgeo.org) GeoServer 用户手册 — GeoServer 2.20.x 用户手册 一、版本需要适配&#xff1a;Geoserver与GeoWebCache、jdk等的版本适配对照 ​ 查看来源 二、准备工作 1、数据&#xff1a;Arcgis标准的切片&#xff0c;通过…...

深度学习-卷积神经网络-基于VGG16模型, 实现猫狗二分类(文末附带数据集下载链接, 长期有效)

简介: 1.基于VGG16模型进行特征提取, 结合mlp实现猫狗二分类 2.训练数据--"dog_cat_class\training_set" 3.模型训练流程 1.对图像数据进行导入和预处理 2.搭建模型, 导入VGG16模型, 去除mlp层, 将经过VGG16训练后的数据作为输入, 输入到自建的mlp层中进行训练, 要…...

计算Java集合占用的空间【详解】

以ArrayList为例&#xff0c;假设集合元素类型是Person类型&#xff0c;假设集合容量为10&#xff0c;目前有两个person对象{name:“Jack”,age12} {name:“Tom”,age14} public class Person{private String name;private int age; }估算Person对象占用的大小&#xff1a; 对…...

仕考网:关于中级经济师考试的介绍

中级经济师考试是一种职称考试&#xff0c;每年举办一次&#xff0c;报名时间在7-8月&#xff0c;考试时间在10-11月 报名入口&#xff1a;中guo人事考试网 报名条件&#xff1a; 1.高中毕业并取得初级经济专业技术资格&#xff0c;从事相关专业工作满10年; 2.具备大学专科…...

SYN590RL 300MHz至450MHz ASK接收机芯片IC

一般描述 SYN590RL是赛诺克全新开发设计的一款宽电压范围,低功耗,高性能,无需外置AGC电容&#xff0c;灵敏度达到典型-110dBm&#xff0c;300MHz”450MHz 频率范围应用的单芯片ASK或OOK射频接收器。 SYN59ORL是一款典型的即插即用型单片高集成度无线接收器&…...

15分钟学 Go 第 20 天:Go的错误处理

第20天&#xff1a;Go的错误处理 目标 学习如何处理错误&#xff0c;以确保Go程序的健壮性和可维护性。 1. 错误处理的重要性 在开发中&#xff0c;错误处理至关重要。程序在运行时可能会出现各种问题&#xff0c;例如文件未找到、网络连接失败等。正确的错误处理能帮助我们…...

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 之均值滤波:图像降噪与模糊的权衡之道

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...