使用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
相关产品推荐
相关产品推荐

