文章标题
Android 封装通用倒计时提示弹窗工具类,支持动态更新文案、自动倒计时关闭,工控 / 自助售卖机项目实战
文章标签
#Android #Dialog 封装 #倒计时弹窗 #工具类 #自助设备开发
正文内容
一、前言
在无人自助设备、工控 Android 项目中,经常需要弹出带自动倒计时关闭的提示弹窗 :操作成功提醒、设备故障提示、取餐超时预警等场景。 如果每个页面单独写 Dialog 会造成大量重复代码,本文封装一套全局可复用CountDownDialogHelper,具备以下特性:
- 线程安全:使用主线程 Handler,支持子线程动态更新弹窗文案
- 自动倒计时:传入总秒数,时间结束自动关闭(可自定义逻辑)
- 全局控制:提供显示 / 销毁 / 判断弹窗状态 / 更新内容方法
- 自定义布局:宽高自适应屏幕 85% 宽度,禁止外部点击关闭,适配工业自助机场景
- 低耦合:构造传入标题、内容、倒计时总时长,一行代码调用
二、完整工具类代码 CountDownDialogHelper
java
package sales.machine.common;
import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import sales.machine.androidproject.R;
public class CountDownDialogHelper {
private final Dialog dialog;
private final TextView tvCountDown;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private int remainSecond;
public boolean isDialogShowing() {
return dialog != null && dialog.isShowing();
}
private final Runnable countDownTask = new Runnable() {
@Override
public void run() {
remainSecond--;
if (remainSecond > 0) {
tvCountDown.setText(remainSecond + "秒后自动关闭");
mainHandler.postDelayed(this, 1000);
} else {
remainSecond = 100;
// dismissDialog();
}
}
};
TextView tvContent = null;
public CountDownDialogHelper(Context context, String title, String content, int totalSecond) {
remainSecond = totalSecond;
// 创建Dialog Theme_Light_NoTitle_Dialog
dialog = new Dialog(context, android.R.style.Theme_Light_NoTitleBar);
View view = LayoutInflater.from(context).inflate(R.layout.dialog_countdown_tip, null);
dialog.setContentView(view);
// 控件绑定
TextView tvTitle = view.findViewById(R.id.tv_dialog_title);
tvContent = view.findViewById(R.id.tv_dialog_content);
tvCountDown = view.findViewById(R.id.tv_dialog_count_down);
// Button btnClose = view.findViewById(R.id.btn_c_close);
tvTitle.setText(title);
tvContent.setText(content);
tvCountDown.setText(remainSecond + "秒后自动关闭");
// 手动关闭
// btnClose.setOnClickListener(v -> dismissDialog());
// 设置弹窗大小(大弹窗)
Window window = dialog.getWindow();
if (window != null) {
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = (int) (context.getResources().getDisplayMetrics().widthPixels * 0.85f); // 屏幕宽度85%
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(lp);
}
// 点击外部不可取消
dialog.setCancelable(false);
}
//更新content
// 更新弹窗提示文案
public void updateContent(String content) {
mainHandler.post(() -> {
if (tvContent != null) {
tvContent.setText(content);
}
});
}
// 显示弹窗 + 启动倒计时
public void show() {
dialog.show();
mainHandler.postDelayed(countDownTask, 1000);
}
// 关闭弹窗,移除倒计时
public void dismissDialog() {
mainHandler.removeCallbacks(countDownTask);
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
三、弹窗布局 XML dialog_countdown_tip.xml
放置于 res/layout/ 目录,适配工控大屏,文字放大适配远距离观看
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="600dp"
android:orientation="vertical"
android:background="#00FF00"
android:padding="40dp">
<!-- 标题 -->
<TextView
android:id="@+id/tv_dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="温馨提示"
android:textSize="22sp"
android:textColor="#ffffff"
android:gravity="center"/>
<!-- 提示内容 -->
<TextView
android:id="@+id/tv_dialog_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="操作成功!"
android:textColor="#FF0000"
android:textSize="100sp"
android:layout_marginTop="20dp"
android:gravity="center"/>
<!-- 倒计时文字 -->
<TextView
android:id="@+id/tv_dialog_count_down"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="5秒后自动关闭"
android:textSize="14sp"
android:textColor="#F53F3F"
android:layout_marginTop="16dp"
android:gravity="center"/>
<!-- 手动关闭按钮 -->
<!-- <Button-->
<!-- android:id="@+id/btn_c_close"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="立即关闭"-->
<!-- android:layout_marginTop="24dp"/>-->
</LinearLayout>
四、工具类使用示例
1. 快速创建并显示弹窗(Activity/Fragment)
java
运行
// 构造:上下文、标题、提示内容、倒计时总秒数5秒
CountDownDialogHelper countDownDialog = new CountDownDialogHelper(this, "操作提示", "取餐码已生成,请前往设备扫码取餐", 5);
// 显示弹窗,开始倒计时
countDownDialog.show();
2. 子线程动态更新弹窗文案
java
运行
new Thread(() -> {
// 模拟网络/设备异步任务
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 子线程直接调用,内部自动切主线程更新UI
countDownDialog.updateContent("饺子制作完成,请尽快取餐!超时将作废");
}).start();
3. 手动关闭弹窗、判断弹窗状态
java
运行
// 判断弹窗是否正在显示
if (countDownDialog.isDialogShowing()) {
// 关闭弹窗,清除倒计时回调,避免内存泄漏
countDownDialog.dismissDialog();
}
五、关键细节与优化点说明
- 内存泄漏防护 关闭弹窗时调用
mainHandler.removeCallbacks(countDownTask)移除延时任务,防止 Activity 销毁后 Handler 持有引用造成泄漏。 - 线程安全设计 所有 UI 更新操作通过
mainHandler.post()切换主线程,子线程可直接调用updateContent()无需额外切换线程。 - 工控设备适配优化
- 弹窗宽度固定为屏幕 85%,大屏不会过小;
- 提示内容字号放大至 100sp,适配自助机远距离观看;
setCancelable(false)禁止误触空白处关闭弹窗,保证用户读取提示。
- 可扩展改造点
- 放开注释的关闭按钮,在构造方法增加回调接口,实现手动关闭监听;
- 修改倒计时结束逻辑,放开
dismissDialog()实现倒计时自动关闭; - 增加弹窗背景、圆角、渐变样式适配 UI 规范;
- 新增回调接口
OnCountDownFinishListener,倒计时结束回调业务逻辑。

