How to use a view (shimmer) as a placeholder for an imageView (Glide)

10.1k views Asked by At

I am using Glide to load images to my imageView (which are inside a recyclerview):

Glide.with(image.context).load(url)
        .error(context.getDrawable(R.drawable.placeholder))
        .into(image)

I see that the Glide library also has a "placeholder" function which gives the ability to load a Drawable to be shown when the image is still being loaded.

On the other hand, for the whole recyclerView, I am using the Facebook Shimmer library to show that the recyclerview is being loaded.

Looking at my app, everything works fine. However, still there is a gap time between when the Shimmer is dismissed (data is fetched) and the image is shown. This is exactly when Placeholder is needed. I am wondering, is there any way to use the Shimmer as Placeholder for the imageView as well? The Placeholder feature in Glide only supports Drawable and the Shimmer is a View.

Is there any way to convert Shimmer to Drawable? or GIF? Or any other suggestion?

3

There are 3 answers

7
Mehdi Satei On BEST ANSWER

Thanks to Mike's comment above: There is a ShimmerDrawable class where you can build a shimmer view as a drawbale which can be used in Glide:

private val shimmer = Shimmer.AlphaHighlightBuilder()// The attributes for a ShimmerDrawable is set by this builder
    .setDuration(1800) // how long the shimmering animation takes to do one full sweep
    .setBaseAlpha(0.7f) //the alpha of the underlying children
    .setHighlightAlpha(0.6f) // the shimmer alpha amount
    .setDirection(Shimmer.Direction.LEFT_TO_RIGHT)
    .setAutoStart(true)
    .build()

// This is the placeholder for the imageView
    val shimmerDrawable = ShimmerDrawable().apply {
        setShimmer(shimmer)
    }


Glide.with(image.context).load(url)
        .placeholder(shimmerDrawable)
        .error(context.getDrawable(R.drawable.placeholder))
        .into(image)
0
MariosP On

In my case i wanted a drawable with rounded corners as a placeholder which was not supported by the library and i found that you can achieve this using a ShapeableImageView from Google Material Designs.

1.Add the Google Material Designs Library into your dependencies 'com.google.android.material:material:1.2.1' with version 1.2.1 and above.

2.In your list item view define your ImageView to be a ShapeableImageView like below:

<com.google.android.material.imageview.ShapeableImageView
   android:id="@+id/shapeImageView"
   android:layout_width="70dp"
   android:layout_height="70dp"
   android:background="@android:color/holo_red_dark"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toTopOf="parent"/>

3.And in RecyclerView.ViewHolder class set the ShapeAppearanceModel of above ShapeableImageView to be CornerFamily.ROUNDED with the corner radius you want in pixels and load the ShimmerDrawable as a placeholder in Glide like below:

//initialize shimmer
val shimmer = Shimmer.ColorHighlightBuilder()
     .setBaseColor(ContextCompat.getColor(itemView.context, R.color.teal_200))
     .setBaseAlpha(0.7f)
     .setHighlightAlpha(0.7f)
     .setHighlightColor(ContextCompat.getColor(itemView.context, R.color.purple_700))
     .setDuration(1800)
     .setDirection(Shimmer.Direction.LEFT_TO_RIGHT)
     .setAutoStart(true)
     .build()

 //create ShimmerDrawable()
 val shimmerDrawable = ShimmerDrawable()
 shimmerDrawable.setShimmer(shimmer)

 //set the ShapeAppearanceModel to CornerFamily.ROUNDED and the radius in pixels
 val radius: Float = dpToPx(itemView.context, 15).toFloat();
 shapeImageView.setShapeAppearanceModel(shapeImageView.getShapeAppearanceModel()
                .toBuilder()
                .setAllCorners(CornerFamily.ROUNDED, radius)
                .build())

 //load url from Glide and add shimmerDrawable as placeholder
 Glide.with(itemView.context).load(item.url)
        .placeholder(shimmerDrawable)
        .into(shapeImageView)

with a helper class to convert the radius from dp to pixels

fun dpToPx(context: Context, dp: Int): Int {
  return (dp * context.resources.displayMetrics.density).toInt()
}

And the result of above will be:

shimmer_rounded

0
Agnaldo Do Nascimento Pereira On

I've develop a library to easy add shimmer/skeleton loading effects. https://github.com/AgnaldoNP/AGSkeletonLoading

There is an explanation on the README.md about how to use it. You don't need to add a bunch of layouts to emulate the skeleton, it is automatically calculated. The layout used to show skeleton is the same show the content.

If you use SkeletonImageView on layout file, you'll just call startLoading() and stopLoading() to control the animation. I hope I have helped you