本文已参与「新人创作礼」活动,一起开启掘金创作之路。
更多内容¶
如何对Coil处理一些通用的自定义用例?
Palette¶
Palette允许从图片中提取突出的颜色. 要创建Palette
, 则需要访问图片的Bitmap
. 有很多方式可以完成这些操作:
通过设置ImageRequest.Listener
和入队ImageRequest
, 可以访问图片的Bitmap:
imageView.load("https://www.example.com/image.jpg") {
// Disable hardware bitmaps as Palette needs to read the image's pixels.
allowHardware(false)
listener(
onSuccess = { _, result ->
// Create the palette on a background thread.
Palette.Builder(result.drawable.toBitmap()).generate { palette ->
// Consume the palette.
}
}
)
}
使用自定义OkHttpClient¶
Coil使用OkHttp来执行全部的网络操作. 在创建ImageLoader
时, 可以指定自定义的OkHttpClient
:
val imageLoader = ImageLoader.Builder(context)
// Create the OkHttpClient inside a lambda so it will be initialized lazily on a background thread.
.okHttpClient {
OkHttpClient.Builder()
.addInterceptor(CustomInterceptor())
.build()
}
.build()
注意
如果已经有了OkHttpClient
, 可以用newBuilder()
来来构建新的客户端来和原来的共享资源.
Headers¶
Header可以通过2种方式中的一种来添加进图片请求. 可以给单个请求设置Header:
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
.setHeader("Cache-Control", "no-cache")
.target(imageView)
.build()
imageLoader.execute(request)
或者创建OkHttp Interceptor
来给ImageLoader
执行的每一个请求添加Header:
class RequestHeaderInterceptor(
private val name: String,
private val value: String
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.header(name, value)
.build()
return chain.proceed(request)
}
}
val imageLoader = ImageLoader.Builder(context)
.okHttpClient {
OkHttpClient.Builder()
// This header will be added to every image request.
.addNetworkInterceptor(RequestHeaderInterceptor("Cache-Control", "no-cache"))
.build()
}
.build()
将内存缓存的Key用作Placeholder¶
将以前请求的MemoryCache.Key
用作子请求的placeholder可能是有用处的, 如果有2张图片相同, 仅仅通过不同的尺寸加载. 举个例子, 如果第一个请求加载加载图片的尺寸是100x100, 第二个请求加载图片的尺寸是500x500, 则可以将第一个图片作为第二个图片的异步加载占位符.
下面是示例应用中该效果的代码:
列表中的图片有意加载成非常少的细节, 渐变被有意减速以高亮这种视觉效果.
要达到这种效果, 需要使用第一个请求的MemoryCache.Key
作为第二个请求的ImageRequest.placeholderMemoryCacheKey
:
// First request
listImageView.load("https://www.example.com/image.jpg")
// Second request (once the first request finishes)
detailImageView.load("https://www.example.com/image.jpg") {
placeholderMemoryCacheKey(listImageView.result.memoryCacheKey)
}
注意
先前版本的Coil会automatically自动地尝试设置这种效果. 这要求图片管道部分在主线程上同步地执行, 由此在版本0.12.0
中被彻底地移除.
共享元素转场¶
共享元素转场允许在Activities
和Fragments
之间执行动画. 推荐在Coil中这样处理:
- 共享元素转场和硬件Bitmap是不兼容的 应该设置
allowHardware(false)
以禁止硬件Bitmap, 无论是对动画起始的ImageView
还是动画结束的View
. 如果不这么设置, 转场动画会抛出java.lang.IllegalArgumentException: Software rendering doesn't support hardware bitmaps
异常. - 将首张图片的
MemoryCache.Key
作为最后图片的placeholderMemoryCacheKey
. 这保证了首张图片用作最后图片的占位符, 这将产生顺滑的转场且没有白闪, 尤其是如图片在内存缓存中的话. - 联合使用
ChangeImageTransform
和ChangeBounds
以达到最佳效果.
RemoteView¶
Coil原生并不提供RemoteView
的Target
, 但可以像这样创建一个:
class RemoteViewsTarget(
private val context: Context,
private val componentName: ComponentName,
private val remoteViews: RemoteViews,
@IdRes private val imageViewResId: Int
) : Target {
override fun onStart(placeholder: Drawable?) = setDrawable(placeholder)
override fun onError(error: Drawable?) = setDrawable(error)
override fun onSuccess(result: Drawable) = setDrawable(result)
private fun setDrawable(drawable: Drawable?) {
remoteViews.setImageViewBitmap(imageViewResId, drawable?.toBitmap())
AppWidgetManager.getInstance(context).updateAppWidget(componentName, remoteViews)
}
}
然后将请求正常地enqueue
/execute
:
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
.target(RemoteViewsTarget(context, componentName, remoteViews, imageViewResId))
.build()
imageLoader.enqueue(request)
Transforming Painters¶
AsyncImage
和AsyncImagePainter
都有placeholder
/error
/fallback
参数接收Painter
. Painter并滑使用Composable灵活但却更快, 这是因为Coil不需要支持subcomposition. 即便如此, 将Painter插入, 伸展, 着色, 或者转换以得到想要的UI也是很必要的. 要完成这个目标, 可以这样做:
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = null,
placeholder = forwardingPainter(
painter = painterResource(R.drawable.placeholder),
colorFilter = ColorFilter(Color.Red),
alpha = 0.5f
)
)
onDraw
可以使用尾置lambda进行覆盖:
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = null,
placeholder = forwardingPainter(painterResource(R.drawable.placeholder)) { info ->
inset(50f, 50f) {
with(info.painter) {
draw(size, info.alpha, info.colorFilter)
}
}
}
)