如何编写带预设时长的自定义时间ProgressBar?30秒进度条实现方案咨询
30秒进度条:Runnable vs For循环,以及自定义时长实现
先回答第一个问题:用Runnable(或CountDownTimer),绝对别用For循环!
Trust me,你绝对不想在UI线程里用For循环做计时更新。如果写个类似for (int i=0; i<=100; i++) { Thread.sleep(300); progressBar.setProgress(i); }的代码,直接会把UI线程卡死——进度条根本不会实时动,甚至APP可能被系统判定为无响应(ANR),因为主线程被阻塞得没法处理界面刷新了。
而用Runnable配合Handler,或者Android官方封装的CountDownTimer(内部本质也是Handler+Runnable),是在主线程的消息队列里异步处理进度更新,完全不会阻塞UI,能让进度条平滑变化。CountDownTimer是更省心的选择,推荐优先用它。
实现带预设时长的自定义进度条
下面是完整的可运行示例,实现用户触发30秒计时、实时更新进度的功能:
第一步:布局文件(activity_main.xml)
先搞定界面,放一个横向进度条和触发按钮:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp" android:gravity="center"> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:progress="0"/> <Button android:id="@+id/startTimerBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="启动30秒计时" android:layout_marginTop="24dp"/> </LinearLayout>
第二步:代码逻辑(Kotlin示例,Java思路完全一致)
import android.os.Bundle import android.os.CountDownTimer import android.widget.Button import android.widget.ProgressBar import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { private lateinit var progressBar: ProgressBar private lateinit var startBtn: Button private var countDownTimer: CountDownTimer? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) progressBar = findViewById(R.id.progressBar) startBtn = findViewById(R.id.startTimerBtn) startBtn.setOnClickListener { // 先取消之前的计时(防止重复点击) countDownTimer?.cancel() // 重置进度条状态 progressBar.progress = 0 startBtn.text = "计时中..." // 启动30秒计时(参数是总毫秒数) startCustomCountdown(30000) } } /** * 自定义时长的倒计时方法 * @param totalMillis 总计时时长(毫秒) */ private fun startCustomCountdown(totalMillis: Long) { // 每100毫秒更新一次进度,让进度条更平滑 val updateInterval = 100L countDownTimer = object : CountDownTimer(totalMillis, updateInterval) { override fun onTick(millisUntilFinished: Long) { // 计算当前进度百分比:(总时长-剩余时长)/总时长 * 100 val progress = ((totalMillis - millisUntilFinished).toFloat() / totalMillis * 100).toInt() progressBar.progress = progress } override fun onFinish() { // 计时结束,拉满进度条 progressBar.progress = 100 startBtn.text = "计时完成!" // 这里可以加计时结束后的逻辑,比如弹窗提示、跳转页面等 } }.start() } override fun onDestroy() { super.onDestroy() // 页面销毁时取消计时,避免内存泄漏 countDownTimer?.cancel() } }
如果一定要用手动Runnable+Handler实现,也可以这么写(不如CountDownTimer简洁,但原理相通):
private val handler = android.os.Handler(android.os.Looper.getMainLooper()) private var currentProgress = 0 private val updateInterval = 300L // 30秒分100次更新,每次间隔300毫秒 private fun startRunnableTimer() { currentProgress = 0 progressBar.progress = 0 startBtn.text = "计时中..." handler.post(object : Runnable { override fun run() { currentProgress++ progressBar.progress = currentProgress if (currentProgress < 100) { handler.postDelayed(this, updateInterval) } else { startBtn.text = "计时完成!" } } }) }
总结
- 绝对避开For循环做UI线程的计时更新,会直接导致界面卡死;优先用
CountDownTimer(官方封装的Runnable方案)。 - 自定义时长只需要传入总毫秒数,在回调里计算进度百分比即可,灵活性拉满。
内容的提问来源于stack exchange,提问作者Majid Ahmadi Jebeli
相关产品推荐
相关产品推荐

