You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin图片编辑器:计算前ProgressBar无法显示问题排查

问题根源分析

你的ProgressBar无法显示,核心原因是UI线程被完全阻塞,导致系统根本没时间绘制ProgressBar:

  1. onStopTrackingTouch方法运行在UI线程中,你设置progressBar.visibility = View.VISIBLE后,立刻执行了耗时的图片处理逻辑——而effectConfigApplyListener.onEffectConfigApply(it)最终会调用modifyPhoto方法,其中的runBlocking(Dispatchers.Default)会阻塞当前调用线程(也就是UI线程),直到所有子协程执行完毕。
  2. UI线程被阻塞期间,系统无法处理任何UI绘制请求,所以ProgressBar的VISIBLE状态还没来得及渲染,你又立刻设置了View.GONE,自然看不到它的影子。

另外,modifyPhoto里的runBlocking用法完全错误:runBlocking会阻塞调用它的线程,哪怕指定了Dispatchers.Default,它也只是在后台线程启动子协程,但调用线程(UI线程)会一直等待所有协程完成,等于把耗时操作的阻塞转嫁到了UI线程上。

解决方案

要解决这个问题,核心是把耗时的图片处理逻辑移到后台线程执行,同时正确控制ProgressBar的可见性:

1. 改造Fragment中的触摸事件处理

使用lifecycleScope(Fragment的生命周期绑定协程)启动后台任务,在主线程更新ProgressBar的可见性:

binding.seekBarEffectVal.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
    override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {}

    override fun onStartTrackingTouch(p0: SeekBar?) {}

    override fun onStopTrackingTouch(seekBar: SeekBar?) {
        seekBar?.let {
            val value = it.progress.toDouble() / it.max
            val effect = IEffectFactory().createEffect(param1!!, value) ?: return
            
            // 启动生命周期绑定的协程
            lifecycleScope.launch {
                // 主线程显示进度条
                binding.progressBarProcessingImage.visibility = View.VISIBLE
                try {
                    // 切换到后台线程执行耗时的图片处理
                    withContext(Dispatchers.Default) {
                        effectConfigApplyListener.onEffectConfigApply(effect)
                    }
                } finally {
                    // 无论成功失败,都在主线程隐藏进度条
                    binding.progressBarProcessingImage.visibility = View.GONE
                }
            }
        }
    }
})

2. 修复图片处理方法的协程使用

把modifyPhoto改成挂起函数,用coroutineScope替代runBlocking——coroutineScope会等待所有子协程完成,但不会阻塞调用线程,只会挂起当前协程:

// 改成suspend函数,允许在协程中调用
override suspend fun modifyPhoto(bitmap: Bitmap): Bitmap {
    val width = bitmap.width
    val height = bitmap.height
    val pixels = IntArray(width * height)
    val newPixels = IntArray(width * height)
    bitmap.getPixels(pixels,0, width, 0, 0, width, height)
    val coef = getCoefficient()

    // 用coroutineScope替代runBlocking,挂起协程而非阻塞线程
    coroutineScope {
        for (y in 0 until height) {
            launch {
                for (x in 0 until width) {
                    val colour = pixels[y * width + x]
                    val red = truncate(Color.red(colour) + coef)
                    val green = truncate(Color.green(colour) + coef)
                    val blue = truncate(Color.blue(colour) + coef)
                    newPixels[y * width + x] = Color.rgb(red, green, blue)
                }
            }
        }
    }

    val newBitmap = Bitmap.createBitmap(width, height, bitmap.config)
    newBitmap.setPixels(newPixels, 0, width, 0, 0, width, height)
    return newBitmap
}

3. 调整监听接口的方法

需要把effectConfigApplyListener的onEffectConfigApply方法也改成挂起函数,这样才能在withContext(Dispatchers.Default)中调用:

interface EffectConfigApplyListener {
    suspend fun onEffectConfigApply(effect: IEffect)
}

然后在MainActivity的实现中,直接调用挂起的modifyPhoto方法即可。

额外优化建议
  • 你当前在modifyPhoto中为每一行都启动一个协程,对于大尺寸图片来说会创建大量协程,反而可能降低性能。可以改用async配合awaitAll,或者直接在后台线程用普通循环处理(如果单线程处理速度足够的话)。
  • 可以考虑用ProgressBar.setProgress来实时更新进度,提升用户体验。

内容的提问来源于stack exchange,提问作者Bartosz Kowalski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:46:21