Glide: Load Drawable But Don't Scale Placeholder
Solution 1:
There's a known Glide issue of placeholders distorting loaded images and vice versa. However I think you are not affected by that.
It sounds like what you want is to show the placeholder drawable with scaleType="center"
and you want the loaded image to be scaleType="fitCenter"
. Here's how you express that in Glide. Add android:scaleType="center"
in the XML and use the following Glide load line:
Glide.with(...).load(...).placeholder(R.drawable....).fitCenter().into(imageView);
The trick is that the placeholder is set via setImageDrawable()
so the ImageView will just display it as usual, but you tell Glide to use the FitCenter
explicitly which will fit the loaded image nicely within the ImageView's laid out size via a Transformation and then set it via setImageDrawable()
. Since the fitted image is a perfect fit, center
will just draw the image covering the whole area of the view.
You can also use .centerCrop()
the same way.
If something is wrong you can try .asBitmap()
and .dontAnimate()
, they help most of the time in one way or another.
Solution 2:
Re: Gregs comment: I gave it another shot (with almost a year of extra XP) and came up with this:
<ImageViewandroid:scaleType="center"... />
similar to my other solution and the following animation wrapper:
...
.fitCenter()
.animate(newPaddingAnimationFactory<>(newDrawableCrossFadeFactory<GlideDrawable>(2000)))
.into(imageView)
;
classPaddingAnimationFactory<T extendsDrawable> implementsGlideAnimationFactory<T> {
privatefinal DrawableCrossFadeFactory<T> realFactory;
@Overridepublic GlideAnimation<T> build(boolean isFromMemoryCache, boolean isFirstResource) {
returnnewPaddingAnimation<>(realFactory.build(isFromMemoryCache, isFirstResource));
}
}
classPaddingAnimation<T extendsDrawable> implementsGlideAnimation<T> {
privatefinal GlideAnimation<? super T> realAnimation;
@Overridepublicbooleananimate(T current, final ViewAdapter adapter) {
intwidth= current.getIntrinsicWidth();
intheight= current.getIntrinsicHeight();
return realAnimation.animate(current, newPaddingViewAdapter(adapter, width, height));
}
}
classPaddingViewAdapterimplementsViewAdapter {
@Overridepublic Drawable getCurrentDrawable() {
Drawabledrawable= realAdapter.getCurrentDrawable();
if (drawable != null) {
intpadX= Math.max(0, targetWidth - drawable.getIntrinsicWidth()) / 2;
intpadY= Math.max(0, targetHeight - drawable.getIntrinsicHeight()) / 2;
if (padX != 0 || padY != 0) {
drawable = newInsetDrawable(drawable, padX, padY, padX, padY);
}
}
return drawable;
}
@OverridepublicvoidsetDrawable(Drawable drawable) {
if (VERSION.SDK_INT >= VERSION_CODES.M && drawable instanceof TransitionDrawable) {
// For some reason padding is taken into account differently on M than before in LayerDrawable// PaddingMode was introduced in 21 and gravity in 23, I think NO_GRAVITY default may play// a role in this, but didn't have time to dig deeper than this.
((TransitionDrawable)drawable).setPaddingMode(TransitionDrawable.PADDING_MODE_STACK);
}
realAdapter.setDrawable(drawable);
}
}
Trivial parts of the implementations are omitted, each class's constructor initializes the fields from arguments. Full code available on GitHub in TWiStErRob/glide-support.
If you're stuck on an older version of Glide (before 3.8.0), the same effect can be achieved by:
.fitCenter()
.placeholder(R.drawable.glide_placeholder)
.crossFade(2000)
.into(newGlideDrawableImageViewTarget(imageView) {
@OverridepublicvoidonResourceReady(GlideDrawable resource,
GlideAnimation<? super GlideDrawable> animation) {
super.onResourceReady(resource, newPaddingAnimation<>(animation));
}
})
Note how the two solutions require the same amount of classes, but the post-3.8.0 solution has better separation of concerns and it can be cached in a variable to prevent allocations.
Solution 3:
I do it like mentioned below:
The idea is to set the scale type to as required by the place holder initially & attach listener to change the scale type again to as required by the downloaded image after the image is downloaded.
//ivBuilderLogo = Target ImageView//Set the scale type to as required by your place holder//ScaleType.CENTER_INSIDE will maintain aspect ration and fit the placeholder inside the image view
holder.ivBuilderLogo.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
//AnimationDrawable is required when you are using transition drawables//You can directly send resource id to glide if your placeholder is static//However if you are using GIFs, it is better to create a transition drawable in xml //& use it as shown in this exampleAnimationDrawable animationDrawable;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
animationDrawable=(AnimationDrawable)context.getDrawable(R.drawable.anim_image_placeholder);
else
animationDrawable=(AnimationDrawable)context.getResources().getDrawable(R.drawable.anim_image_placeholder);
animationDrawable.start();
Glide.with(context).load(logo_url)
.placeholder(animationDrawable)
.listener(newRequestListener<String, GlideDrawable>() {
@OverridepublicbooleanonException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource)
{
returnfalse;
}
//This is invoked when your image is downloaded and is ready //to be loaded to the image view@OverridepublicbooleanonResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource)
{
//This is used to remove the placeholder image from your ImageView //and load the downloaded image with desired scale-type(FIT_XY in this case)//Changing the scale type from 'CENTER_INSIDE' to 'FIT_XY' //will stretch the placeholder for a (very) short duration,//till the downloaded image is loaded//setImageResource(0) removes the placeholder from the image-view //before setting the scale type to FIT_XY and ensures that the UX //is not spoiled, even for a (very) short duration
holder.ivBuilderLogo.setImageResource(0);
holder.ivBuilderLogo.setScaleType(ImageView.ScaleType.FIT_XY);
returnfalse;
}
})
.into( holder.ivBuilderLogo);
My transition drawable (R.drawable.anim_image_placeholder) :
(not required if using a static image)
<?xml version="1.0" encoding="utf-8"?><animation-listxmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="false"><itemandroid:drawable="@drawable/loading_frame1"android:duration="100" /><!--<item android:drawable="@drawable/loading_frame2" android:duration="100" />--><itemandroid:drawable="@drawable/loading_frame3"android:duration="100" /><!--<item android:drawable="@drawable/loading_frame4" android:duration="100" />--><itemandroid:drawable="@drawable/loading_frame5"android:duration="100" /><!--<item android:drawable="@drawable/loading_frame6" android:duration="100" />--><itemandroid:drawable="@drawable/loading_frame7"android:duration="100" /><!--<item android:drawable="@drawable/loading_frame8" android:duration="100" />--><itemandroid:drawable="@drawable/loading_frame9"android:duration="100" /><!--<item android:drawable="@drawable/loading_frame10" android:duration="100" />--></animation-list>
Post a Comment for "Glide: Load Drawable But Don't Scale Placeholder"