Recipes¶
This page provides guidance on how to handle some common use cases with Coil. You might have to modify this code to fit your exact requirements, but it should hopefully give you a push in the right direction!
See a common use case that isn't covered? Feel free to submit a PR with a new section.
Palette¶
Palette allows you to extract prominent colors from an image. To create a Palette
, you'll need access to an image's Bitmap
. This can be done in a number of ways:
You can get access to an image's bitmap by setting a ImageRequest.Listener
and enqueuing an ImageRequest
:
imageView.load("https://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.
}
}
)
}
Using a Memory Cache Key as a Placeholder¶
Using a previous request's MemoryCache.Key
as a placeholder for a subsequent request can be useful if the two images are the same, though loaded at different sizes. For instance, if the first request loads the image at 100x100 and the second request loads the image at 500x500, we can use the first image as a synchronous placeholder for the second request.
Here's what this effect looks like in the sample app:
Images in the list have intentionally been loaded with very low detail and the crossfade is slowed down to highlight the visual effect.
To achieve this effect, use the MemoryCache.Key
of the first request as the ImageRequest.placeholderMemoryCacheKey
of the second request. Here's an example:
// First request
listImageView.load("https://example.com/image.jpg")
// Second request (once the first request finishes)
detailImageView.load("https://example.com/image.jpg") {
placeholderMemoryCacheKey(listImageView.result.memoryCacheKey)
}
Shared Element Transitions¶
Shared element transitions allow you to animate between Activities
and Fragments
. Here are some recommendations on how to get them to work with Coil:
-
Shared element transitions are incompatible with hardware bitmaps. You should set
allowHardware(false)
to disable hardware bitmaps for both theImageView
you are animating from and the view you are animating to. If you don't, the transition will throw anjava.lang.IllegalArgumentException: Software rendering doesn't support hardware bitmaps
exception. -
Use the
MemoryCache.Key
of the start image as theplaceholderMemoryCacheKey
for the end image. This ensures that the start image is used as the placeholder for the end image, which results in a smooth transition with no white flashes if the image is in the memory cache. -
Use
ChangeImageTransform
andChangeBounds
together for optimal results.
Using Compose? Check out this article for how to perform shared element transitions with AsyncImage
.
Remote Views¶
Coil does not provide a Target
for RemoteViews
out of the box, however you can create one like so:
class RemoteViewsTarget(
private val context: Context,
private val componentName: ComponentName,
private val remoteViews: RemoteViews,
@IdRes private val imageViewResId: Int
) : Target {
override fun onStart(placeholder: Image?) = setDrawable(placeholder)
override fun onError(error: Image?) = setDrawable(error)
override fun onSuccess(result: Image) = setDrawable(result)
private fun setDrawable(image: Image?) {
remoteViews.setImageViewBitmap(imageViewResId, image?.toBitmap())
AppWidgetManager.getInstance(context).updateAppWidget(componentName, remoteViews)
}
}
Then enqueue
/execute
the request like normal:
val request = ImageRequest.Builder(context)
.data("https://example.com/image.jpg")
.target(RemoteViewsTarget(context, componentName, remoteViews, imageViewResId))
.build()
imageLoader.enqueue(request)
Transforming Painters¶
Both AsyncImage
and AsyncImagePainter
have placeholder
/error
/fallback
arguments that accept Painter
s. Painters are less flexible than using composables, but are faster as Coil doesn't need to use subcomposition. That said, it may be necessary to inset, stretch, tint, or transform your painter to get the desired UI. To accomplish this, copy this Gist into your project and wrap the painter like so:
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = null,
placeholder = forwardingPainter(
painter = painterResource(R.drawable.placeholder),
colorFilter = ColorFilter(Color.Red),
alpha = 0.5f,
),
)
The onDraw
can be overwritten using a trailing lambda: