Open lfielke opened 5 years ago
Thanks! I've thought about adding a placeholder(data)
method, however have shied away from it so far. Adding it would create more work on the main thread, which could reduce performance. Also, it could result in some unexpected behaviour where you expect something to be in the memory cache and it isn't.
Good points. It sounds like what I'm talking about isn't really a "placeholder", because Coil assumes that placeholders are available straight away, synchronously on the main thread. Have I understood that correctly?
Thinking about this overnight, could you do two loads
one-after the other, the first one loading the thumbnail URL but specifying a read only cache policies?
Something like this:
imageView.load(thumbUrl) {
diskCachePolicy = READ_ONLY
networkCachePolicy = DISABLED
placeholder = R.drawable.image_placeholder
crossfade(true) // crossfade from image_placeholder to thumbnail if it loads from disk
}
imageView.load(fullSizeUrl) {
crossfade(true) // crossfade from thumbnail to full image
}
These requests could technically race, which could be a problem in general with this approach. I'm not sure what the desired behaviour even is when you have two loads for the same target. In this example, you want the first load to proceed, but only until the second load completes. But loading into a RecyclerView you want the opposite, where the first load should be cancelled as soon as the view is reused and the second load starts.
Ah I see what you mean. This would add a decent chunk of complexity to RealImageLoader
so I'm not sure I'd want to add this in. However, you could create the same effect with get
:
coroutineScope.launch(Dispatchers.Main.immediate) {
val thumb = async { imageLoader.get(thumbUrl) }
val fullSize = async { imageLoader.get(fullSizeUrl) }
fullSize.invokeOnCompletion { thumb.cancel() }
// There's probably a better way to do this.
val thumbDrawable = try {
thumb.await()
} catch (e: CancellationException) {
null
}
imageView.setDrawable(thumbDrawable)
val fullSizeDrawable = fullSize.await()
imageView.setDrawable(fullSizeDrawable)
}
Replace imageLoader
with Coil
if you use the singleton. To figure out whether or not to crossfade, you can add a Request.Listener
and check the returned DataSource
in onSuccess
.
Disclaimer: the above code is completely untested and unoptimized.
I'd like something similar. I'd like to load image from cache (almost) immediately, but also check the network in the background for change.
Our image server supports ETag, and 304 Unchaged response. But because the URL is still the same, Coil will store the image in memory, and never asks the network again, so the user does not see the updated image. So I have the memory cache disabled, but then, when scrolling long list up and down, the images are always blank first and load later, which is distracting.
Just to check if this is already supported? In glide, I can do as below (placeholder -> initial-fast-load -> full-load)
With codes as below
val requestOption = RequestOptions()
.placeholder(R.drawable.placeholder).centerCrop()
Glide.with(this).load(fullImageUrl)
.transition(DrawableTransitionOptions.withCrossFade())
.thumbnail(Glide.with(this)
.load(fastLoadUrl)
.apply(requestOption))
.apply(requestOption)
.into(my_image_view)
Refer to this article https://medium.com/mobile-app-development-publication/glide-image-loader-the-basic-798db220bb44
Hopes Coil
can do the same.
Still waiting for this :(
I would also like to see this feature implemented. I've created a library named AirBrush that uses this feature in Glide, but I'm planning to support Coil due to JetPack Compose. I can hack around it and use TransitionDrawable
but I would prefer to use the API :)
This feature is the only thing keeping me using Glide for bigger projects. Would love to see a way to do that using Coil
can anyone suggest any workaround for the coil to achieve the same?
can anyone suggest any workaround for the coil to achieve the same?
Here you go. It is used by me in my prod app and no issues/complaints till now. If anyone finds a better solution, please let me know.
can anyone suggest any workaround for the coil to achieve the same?
Slight changes to kasem-sm's reply:
fun ImageView.loadWithQuality(
highQuality: String,
lowQuality: String,
placeholderRes: Int? = null,
errorRes: Int? = null
) {
placeholderRes?.let {
setImageResource(placeholderRes)
}
var isHighQualityLoaded = false
class CallbackImageViewTarget(val callback: () -> Boolean) : ImageViewTarget(this) {
override fun onSuccess(result: Drawable) {
if (callback()) {
super.onSuccess(result)
}
}
}
val lowQualityRequest = ImageRequest.Builder(context).apply {
data(lowQuality)
target(CallbackImageViewTarget(
callback = {
return@CallbackImageViewTarget !isHighQualityLoaded
}
))
}.build()
val lowQualityLoader = context.imageLoader.enqueue(lowQualityRequest)
val highQualityRequest = ImageRequest.Builder(context).apply {
data(highQuality)
errorRes?.let { error(errorRes) }
target(CallbackImageViewTarget(
callback = {
isHighQualityLoaded = true
if (!lowQualityLoader.isDisposed) {
lowQualityLoader.dispose()
}
return@CallbackImageViewTarget true
}
))
}.build()
context.imageLoader.enqueue(highQualityRequest)
}
@0n4li Hey, thanks for improving that code 😊
Probably the below code works better. Need to test:
fun ImageView.loadWithQuality(
highQuality: String,
lowQuality: String,
placeholderRes: Int? = null,
errorRes: Int? = null
) {
load(lowQuality) {
placeholderRes?.let { placeholder(placeholderRes) }
listener(onSuccess = { _, _ ->
load(highQuality) {
placeholder(drawable) // If there was a way to not clear existing image before loading, this would not be required
errorRes?.let { error(errorRes) }
}
})
}
}
Is there any plan to support this feature?
This feature is the only thing keeping me using Glide for bigger projects. Would love to see a way to do that using Coil
same Here :'(
any update on this ??
val requestBuilder = Glide.with(context)
.asDrawable()
.sizeMultiplier(0.5f)
GlideImage(
model = contentUri,
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
){
it
.override(this.maxWidth.value.dpToPx(), this.maxHeight.value.dpToPx())
.thumbnail(requestBuilder)
}
does coil support thumbnail size loading as glide does? when i load lot of image in grid in compose, it seems to be delayed compared to scroll action while loading image.
Any updates on this? Interrested in the way to implement this behaviour with AsyncImage in compose)
I tried this and it worked for me in compose:
AsyncImage(
model = "high resolution image",
placeholder = rememberAsyncImagePainter("low resolution image"),
contentScale = ContentScale.Crop,
contentDescription = "",
)
Anyone have an idea on how to do this? Maybe we can throw up an MR to add this support.
First up, congrats on shipping!
A feature request for consideration. Sometimes an app will show a list of small image thumbnails, which are clickable to open the image full screen. The thumbnails are physically small (in dimension and file size) so the list of thumbnails loads quickly. The full images (loaded from a different URL to the thumbnail) are much bigger, so might take a few seconds to load from the network.
Currently, Coil supports specifying a placeholder
Drawable
using theplaceholder
method. It would be good if that was extended to allow passing in the URL of the thumbnail, which presumably would still be in the memory cache because it was being displayed on the previous screen.The desired effect would look like the image sampling mentioned on the docs website. So the low-res thumbnail would be shown until the full-res image has loaded. The API difference being the first low-res image has come from a different URL, rather than the same one.
I've used "URL" above to keep the wording simple, but I mean all the different "sources" in general that are parameters to the
ImageLoader.load
methods (String
,Uri
,HttpUrl
, etc), or theloadAny
method that uses a Mapper.