【问题标题】:Glide: load drawable but don't scale placeholderGlide:加载可绘制但不缩放占位符
【发布时间】:2015-11-21 00:16:54
【问题描述】:

有没有办法使用 Glide 来分配一个占位符但保持这个图像的原始比例? 我有一个大小可变的ImageView(取决于传入的图像),我在调用Glide.with().load().into() 之前设置了它,我想为它使用一个占位符,但不希望将占位符调整为@987654323 的大小@,我希望它保持原来的大小。

到目前为止,我还没有找到方法。

【问题讨论】:

    标签: android image drawable android-glide


    【解决方案1】:

    有一个known Glide issue of placeholders distorting loaded images and vice versa。但是我认为你不会受到影响。

    听起来您想要使用scaleType="center" 显示可绘制的占位符,并且您希望加载的图像为scaleType="fitCenter"。以下是您在 Glide 中的表达方式。在 XML 中添加 android:scaleType="center" 并使用以下 Glide 加载线:

    Glide.with(...).load(...).placeholder(R.drawable....).fitCenter().into(imageView);
    

    诀窍是占位符是通过setImageDrawable() 设置的,所以 ImageView 会像往常一样显示它,但是你告诉 Glide 明确使用 FitCenter 它将很好地适应加载的图像在 ImageView 的布局大小内一个转换,然后通过setImageDrawable() 设置它。由于拟合后的图像非常合适,center 只会绘制覆盖整个视图区域的图像。

    你也可以用同样的方式使用.centerCrop()

    如果出现问题,您可以尝试 .asBitmap().dontAnimate(),它们在大多数情况下都会以某种方式提供帮助。

    【讨论】:

    • 这个解决方案的问题:虽然它最初看起来可以工作,但请注意在过渡期间,当 Glide 将占位符淡化为真实图像时,占位符图像被放大到 fitCenter。这是一个大问题,因为我试图在这里保留这种淡入淡出过渡。所以 dontAnimate() 违背了尝试这样做的目的。有什么想法吗?
    【解决方案2】:

    回复:Gregs comment: 我又试了一次(几乎一年的额外经验)并想出了这个:

    <ImageView android:scaleType="center" ... />
    

    类似于我的其他解决方案和以下动画包装器:

    ...
        .fitCenter()
        .animate(new PaddingAnimationFactory<>(new DrawableCrossFadeFactory<GlideDrawable>(2000)))
        .into(imageView)
    ;
    
    class PaddingAnimationFactory<T extends Drawable> implements GlideAnimationFactory<T> {
        private final DrawableCrossFadeFactory<T> realFactory;
        @Override public GlideAnimation<T> build(boolean isFromMemoryCache, boolean isFirstResource) {
            return new PaddingAnimation<>(realFactory.build(isFromMemoryCache, isFirstResource));
        }
    }
    
    class PaddingAnimation<T extends Drawable> implements GlideAnimation<T> {
        private final GlideAnimation<? super T> realAnimation;
        @Override public boolean animate(T current, final ViewAdapter adapter) {
            int width = current.getIntrinsicWidth();
            int height = current.getIntrinsicHeight();
            return realAnimation.animate(current, new PaddingViewAdapter(adapter, width, height));
        }
    }
    
    class PaddingViewAdapter implements ViewAdapter {
        @Override public Drawable getCurrentDrawable() {
            Drawable drawable = realAdapter.getCurrentDrawable();
            if (drawable != null) {
                int padX = Math.max(0, targetWidth - drawable.getIntrinsicWidth()) / 2;
                int padY = Math.max(0, targetHeight - drawable.getIntrinsicHeight()) / 2;
                if (padX != 0 || padY != 0) {
                    drawable = new InsetDrawable(drawable, padX, padY, padX, padY);
                }
            }
            return drawable;
        }
        @Override public void setDrawable(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);
        }
    }
    

    省略了实现的琐碎部分,每个类的构造函数都从参数初始化字段。GitHub in TWiStErRob/glide-support 上提供了完整代码。


    如果您卡在旧版本的 Glide(3.8.0 之前),可以通过以下方式实现相同的效果:

    .fitCenter()
    .placeholder(R.drawable.glide_placeholder)
    .crossFade(2000)
    .into(new GlideDrawableImageViewTarget(imageView) {
        @Override public void onResourceReady(GlideDrawable resource,
                GlideAnimation<? super GlideDrawable> animation) {
            super.onResourceReady(resource, new PaddingAnimation<>(animation));
        }
    })
    

    请注意,这两种解决方案需要相同数量的类,但 3.8.0 后的解决方案具有更好的关注点分离,并且可以缓存在变量中以防止分配。

    【讨论】:

    • 此解决方案适用于我们的 android 5.x 及更低版本。然而,在 6 和 N 上,看起来 PaddingAnimation 的行为不像预期的那样,我们再次看到行为占位符在过渡到 centerCrop 视图期间增长。我们将进行研究,但如果其他人看到 6 和 N 的故障,我很想听听找到的任何解决方案。
    • @JakeHall 你能分享一下它的屏幕截图吗?如果在独立的 ImageView 中使用 InsetDrawable 看起来是否如您所愿?您的占位符是否可能在这些版本之间存在不一致的大小(即较新的版本 -> 更高的 dpi -> 不同的资产)。我认为数学和scaleType=center 仅在占位符小于视图/满载资源时才有效;请注意,例如 padXY 可能变为负数。
    • @TWiStErRob 给你,一个来自我们的应用程序,您可以看到占位符动画更大,另一个来自演示应用程序,您可以看到占位符弹出到更大的尺寸。这只发生在 23+,在较低版本的 android 上,动画按设计工作。 dropbox.com/s/3mzco9coiokyoa1/transition_issue.mp4?dl=0dropbox.com/s/hgzfv3njfg0egq3/transition_2.mov?dl=0
    • @TWiStErRob 谢谢!更新效果很好,你是最棒的。
    • @TWiStErRob,你是真正的英雄!我更新了 Kotlin 和 Glide 4+ 中的代码,其中“Animation”被重命名为 Transition。 gist.github.com/sevar83/03fc54baa15967f69bf58b6229526946
    【解决方案3】:

    我会像下面提到的那样做:

    想法是最初将比例类型设置为占位符的要求并附加侦听器以在下载图像后再次将比例类型更改为下载图像的要求。

    //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 example
    AnimationDrawable 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(new RequestListener<String, GlideDrawable>() {
                            @Override
                            public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource)
                            {
                               return false;
                            }
    
                            //This is invoked when your image is downloaded and is ready 
                            //to be loaded to the image view
                            @Override
                            public boolean onResourceReady(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);
                                return false;
                            }
                        })
                        .into( holder.ivBuilderLogo);
    

    我的过渡可绘制(R.drawable.anim_image_placeholder):

    (如果使用静态图像则不需要)

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:oneshot="false">
        <item android:drawable="@drawable/loading_frame1" android:duration="100" />
        <!--<item android:drawable="@drawable/loading_frame2" android:duration="100" />-->
        <item android:drawable="@drawable/loading_frame3" android:duration="100" />
        <!--<item android:drawable="@drawable/loading_frame4" android:duration="100" />-->
        <item android:drawable="@drawable/loading_frame5" android:duration="100" />
        <!--<item android:drawable="@drawable/loading_frame6" android:duration="100" />-->
        <item android:drawable="@drawable/loading_frame7" android:duration="100" />
        <!--<item android:drawable="@drawable/loading_frame8" android:duration="100" />-->
        <item android:drawable="@drawable/loading_frame9" android:duration="100" />
        <!--<item android:drawable="@drawable/loading_frame10" android:duration="100" />-->
    </animation-list>
    

    【讨论】:

    • 您的解决方案帮助了我
    • 感觉如果你只是做了.animate(android.R.anim.fade_in) 而没有任何scaleType hacks,你可以实现相同的行为,但是我没有测试这个理论。我认为这个简单的动画会起作用,因为setImageResource 基本上清除了ImageView,所以.crossFade() 已经被禁用,它会退回到淡入(参见DrawableCrossFadeFactory.DefaultAnimationFactoryDrawableCrossFadeViewAnimation.animate)。
    猜你喜欢
    • 2019-01-24
    • 1970-01-01
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多