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

使用Coil加载网络图片时如何展示进度条?

Coil加载网络图片时实现进度条展示的方案

实现原理

Coil底层默认基于OkHttp发起网络资源请求,本身没有内置进度回调能力,因此可以通过自定义OkHttp拦截器捕获图片下载的字节流进度,将进度同步到UI层更新进度条即可实现需求。

完整实现步骤

  • 第一步:定义进度回调接口与进度拦截器
// 进度回调接口
interface ProgressListener {
    fun onProgress(url: String, progress: Int, isComplete: Boolean)
}

// 自定义进度拦截器
class ProgressInterceptor(private val listener: ProgressListener) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val originalResponse = chain.proceed(originalRequest)
        val targetUrl = originalRequest.url.toString()
        val totalLength = originalResponse.body.contentLength()
        return originalResponse.newBuilder()
            .body(ProgressResponseBody(targetUrl, originalResponse.body, totalLength, listener))
            .build()
    }

    // 自定义包装类,监听字节流读取进度
    private class ProgressResponseBody(
        private val url: String,
        private val originBody: ResponseBody,
        private val totalLength: Long,
        private val listener: ProgressListener
    ) : ResponseBody() {
        private var bufferedSource: BufferedSource? = null

        override fun contentType() = originBody.contentType()
        override fun contentLength() = originBody.contentLength()

        override fun source(): BufferedSource {
            if (bufferedSource == null) {
                bufferedSource = Okio.buffer(wrapSource(originBody.source()))
            }
            return bufferedSource!!
        }

        private fun wrapSource(source: Source): Source {
            return object : ForwardingSource(source) {
                var downloadedLength = 0L
                override fun read(sink: Buffer, byteCount: Long): Long {
                    val readCount = super.read(sink, byteCount)
                    downloadedLength += if (readCount != -1L) readCount else 0
                    val progress = if (totalLength > 0) {
                        (downloadedLength * 100 / totalLength).toInt()
                    } else 0
                    listener.onProgress(url, progress, readCount == -1L)
                    return readCount
                }
            }
        }
    }
}
  • 第二步:给Coil配置带进度拦截器的OkHttpClient
// 建议在Application的onCreate方法中全局初始化一次
val customImageLoader = ImageLoader.Builder(context)
    .okHttpClient {
        OkHttpClient.Builder()
            .addInterceptor(ProgressInterceptor(GlobalProgressHandler))
            .build()
    }
    .build()
Coil.setImageLoader(customImageLoader)
  • 第三步:页面层绑定进度回调,更新进度条UI
val imageUrl = "需要加载的目标图片URL"
// 初始化进度条状态
progressBar.visibility = View.VISIBLE
progressBar.progress = 0

// 定义当前页面的进度回调
val pageListener = object : ProgressListener {
    override fun onProgress(url: String, progress: Int, isComplete: Boolean) {
        // 匹配当前加载的URL,避免多请求串扰
        if (url != imageUrl) return
        // 切换到主线程更新UI
        runOnUiThread {
            progressBar.progress = progress
            if (isComplete || progress == 100) {
                progressBar.visibility = View.GONE
            }
        }
    }
}

// 执行图片加载
targetImageView.load(imageUrl) {
    listener(
        onError = { _, _ ->
            // 加载失败隐藏进度条
            progressBar.visibility = View.GONE
        }
    )
}

注意事项

  • 如果图片已经存在于Coil的磁盘缓存中,不会触发网络请求,进度会直接跳到100,此时直接隐藏进度条即可
  • 多图片同时加载的场景下,除了通过URL匹配进度外,也可以给每个请求增加唯一标识来区分回调
  • 该方案对普通位图、GIF、SVG等所有网络资源格式的加载都生效,不需要额外适配

内容的提问来源于stack exchange,提问作者Unaisul Hadi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 08:45:01