I am using Glide to load images to my activity (inside a recycler view) and I want to use a Lottie loading animation file as a place holder.
so this is my glide code:
Glide.with(mContext)
.load(PATH_TO_FILE)
.placeholder(createLottieDrawable("loading_animation.json"))
.into(holder.img_view);
and this is the method createLottieDrawable
which I use to create a drawable from the lottie file:
private LottieDrawable createLottieDrawable(String filename) {
final LottieDrawable lottieDrawable = new LottieDrawable();
LottieComposition.Factory.fromAssetFileName(mContext, filename,
composition -> {
lottieDrawable.setComposition(composition);
lottieDrawable.loop(true);
lottieDrawable.playAnimation();
});
return lottieDrawable;
}
but it doesn't work and I don't see anything as a placeholder.
Any idea why?
Just enter the URL to your Lottie file. You can play around with the options to customize the player or animation. Then copy the code and paste it into your website's HTML. You can also click Use this animation in <html> on any animation page to automatically generate the code.
Placeholder. Placeholders are Drawables that are shown while a request is in progress. When a request completes successfully, the placeholder is replaced with the requested resource. If the requested resource is loaded from memory, the placeholder may never be shown.
Step 1: Add this dependency into the App level gradle module of the project and then sync the gradle with the project. This library enables us to use Lottie's animations: Java.
This answer provides an alternative approach solution to your problem and doesn't directly answer it.
As I mentioned before in my comment above I would suggest you use a LottieAnimationView
for loading animations instead of the placeholder approach. It is more robust and Lottie docs suggest it for loading animations. The way I did it is I placed the LottieAnimationView
on top of the image I'm loading and play the animation by default. I attach a RequestListener
with addListener()
to the builder methods, then stop the animation and set the LottieAnimationView
's visibility to GONE inside onResourceReady()
callback of the listener.
Inside your list_item.xml put your LottieAnimationView
on top of your Imageview
by nesting them inside a FrameLayout
like so:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/list_item_image"
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_cover_image_height"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="centerCrop" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/image_pending_animation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
app:lottie_autoPlay="true"
app:lottie_fileName="image-loading.json"
app:lottie_loop="true"
app:lottie_scale="2" />
</FrameLayout>
Then in your adapter class's onBindViewHolder()
method you can load the image and attach a RequestListener
to know when it's ready like this:
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
://other code omitted
://holder.cover is the ImageView
://holder.pendingImage is the LottieAnimationView
GlideApp.with(context)
.load(item.getImageUri())
.addListener(imageLoadingListener(holder.pendingImage))//this line
.into(holder.cover);
}
The following is an implementation of the RequestListener, I made it public because I use it somewhere else:
public static RequestListener<Drawable> imageLoadingListener(final LottieAnimationView pendingImage) {
return new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
//hide the animation
pendingImage.pauseAnimation();
pendingImage.setVisibility(View.GONE);
return false;//let Glide handle everything else
}
};
}
NB: The Type on the RequestListener can also be Bitmap, like RequestListener<Bitmap>
just make sure to change the parameters accordingly.
Based on Edgar's response above, I translated this using DataBinding and it's really quick to implement and use.
First off, wherever you have your image being loaded from a URL, add a LottieAnimationView over it.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="140dp"
app:loader="@{lavLoader}"
app:url="@{horizontalCardsItemVM.imageUrl}" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lav_loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/iv_image"
android:layout_alignTop="@id/iv_image"
android:layout_alignEnd="@id/iv_image"
android:layout_alignBottom="@id/iv_image"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="@raw/image_loader" />
</FrameLayout>
As you can see above, I'm passing a reference of the LottieAnimationView to the ImageView using app:loader="@{lavLoader}"
This reference is used in a BindingAdapter. Now in a BindingAdapter (defined statically anywhere you want), use the addListener method as mentioned by Edgar:
@BindingAdapter("url", "loader")
fun setImageSrcFromUrlWithLoader(view: ImageView, url: String?, loader: LottieAnimationView) {
url?.let {
Glide.with(view)
.load(it)
.addListener(imageLoadingListener(loader))
.centerCrop()
.into(view)
}
}
And the imageLoadingListener function is the same as the one written by Edgar:
fun imageLoadingListener(pendingImage: LottieAnimationView): RequestListener<Drawable?>? {
return object : RequestListener<Drawable?> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: com.bumptech.glide.request.target.Target<Drawable?>?, isFirstResource: Boolean): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: com.bumptech.glide.request.target.Target<Drawable?>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
pendingImage.pauseAnimation()
pendingImage.visibility = View.GONE
return false
}
}
}
The benefit of using this approach is now you don't need to do much in any of your actual layouts, except add the LottieAnimationView and link it to the ImageView. Once the setup is done, it's a matter of seconds to include this logic everywhere without making any tweaks to Java/Kotlin code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With