Android Volley - 如何动画加载图像?

13

有没有办法在图像加载时播放淡入动画?现在它只是突然出现。我正在使用Volley工具包中的NetworkImageView。

此外,是否有一种方法可以在不使用ImageLoader.get( .. )的情况下设置网络图像视图上的加载和错误位图?

谢谢!

//编辑好的,感谢您所有人的帮助,但如果我们想要完美主义者,我们应该只有在从磁盘缓存中加载时才执行动画,覆盖setImageBitmap会导致即使从 memcache 中提取也会触发动画

你想做的是将一个布尔值shouldAnimate添加到ImageListener.onResponse中,如下所示

public static ImageListener getImageListener(final ImageView view, final int defaultImageResId,
        final int errorImageResId) {
    return new ImageListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            if (errorImageResId != 0) {
                view.setImageResource(errorImageResId);
            }
        }

        @Override
        public void onResponse(ImageContainer response, boolean isImmediate, boolean shouldAnimate) {
            if (response.getBitmap() != null) {
                if (shouldAnimate) {
                    // ADDED
                    view.setAlpha(0f);
                    view.setImageBitmap(response.getBitmap());
                    view.animate().alpha(1f).setDuration(1000);
                    // END ADDED
                } else {
                    view.setImageBitmap(response.getBitmap());
                }
            } else if (defaultImageResId != 0) {
                view.setImageResource(defaultImageResId);
            }
        }
    };
}

这是一种设置位图的方法,不管它来自哪里,因此在 ImageLoader 中除了某些情况外,每次使用都需要将其设置为 false。

class BatchedImageRequest {

   private void batchResponse(String cacheKey, BatchedImageRequest request,
        final VolleyError error) {
      ...
      container.mListener.onResponse(container, false, true);
      ...
   }
}

我为复制和粘贴创建了一个Gist - https://gist.github.com/ursusursus/5732521


不需要 shouldAnimate 布尔值,因为根据你的逻辑,“从内存缓存中加载时不进行动画”,实际上 shouldAnimate == !isImmediate - Itai Hanski
Picasso 做得很好,检查图片是从缓存还是网络获取,然后进行动画处理。 - Vipin Sahu
使用Volley的Android动画初学者指南,用于Network Imageview的Picasso补丁 - Vipin Sahu
6个回答

23

可以在这里找到CommonsWare答案的示例实现:https://gist.github.com/benvd/5683818

使用TransitionDrawable确实会增加一个额外的过度绘制层。如果你想避免这种情况,也许使用ViewPropertyAnimator可能会有所帮助。

其要点基本上是在setImageBitmap()中具有以下片段:

TransitionDrawable td = new TransitionDrawable(new Drawable[]{
        new ColorDrawable(android.R.color.transparent),
        new BitmapDrawable(getContext().getResources(), bm)
});

setImageDrawable(td);
td.startTransition(FADE_IN_TIME_MS);

过度绘制只会在实际转换期间发生吗?我希望TransitionDrawable足够明智,只在结束状态下显示其中一个,而不会出现过度绘制,但我个人没有进行过实验。 - CommonsWare
顺便问一下,即使我们从 memCache 中获取,这个动画也会播放吗? - urSus
当图像从缓存中提取时,动画也会发生。是的。 - benvd
2
+1 支持旧版 Android。如果您想避免在缓存加载时进行动画处理,可以将此代码放入 ImageListener 的实现中,并在 if (!isImmediate) 语句内执行。 - Itai Hanski
2
看一下TransitionDrawable的源代码,似乎第一个drawable始终保持不变,除非你启用了交叉淡入淡出。如果禁用交叉淡入淡出(默认情况下),则可以在顶部的可绘制区域上具有透明区域,您可以透过它们看到下面的可绘制物。因此,当动画完成时,使用setCrossFadeEnabled(true)应该能够消除您的过度绘制问题。https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/graphics/java/android/graphics/drawable/TransitionDrawable.java - Scott W
显示剩余2条评论

6
有什么办法可以在图像加载时播放淡入动画吗?现在它只是闪烁到位。我正在使用Volley工具包中的NetworkImageView。
草率地说,创建自己的NetworkImageView子类,并覆盖setImageBitmap()来设置当应用图像时的alpha动画。
此外,有没有一种方法可以在不使用ImageLoader.get(..)的情况下设置网络图像视图上的加载和错误位图?
调用setImageBitmap()或setImageResource(),就像使用常规ImageView一样。

是的,这正是我所想的,我只是想知道是否有内置的东西,因为除了一些IO片段之外,几乎没有关于如何使用该库的示例。 - urSus
1
@benvd 你可能想把那个变成一个答案。 - urSus

5
这是我实现的一种动画 NetworkImageView。它使用 ObjectAnimator 来淡入新下载的图像,而缓存的图像则立即显示。实现相当简单 - 只需要覆盖两个 NetworkImageView 方法(setImageBitmap(Bitmap) 和 setImageUrl(String, ImageLoader))。它使用提供的 ImageLoader 来确定图像是否来自缓存。
public class AnimatedNetworkImageView extends NetworkImageView {

    private static final int ANIM_DURATION = 500;
    private boolean shouldAnimate = false;

    public AnimatedNetworkImageView(Context context) {
        super(context);
    }

    public AnimatedNetworkImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public AnimatedNetworkImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        if(shouldAnimate) {
            ObjectAnimator.ofFloat(this, "alpha", 0, 1).setDuration(ANIM_DURATION).start();
        }
    }

    @Override
    public void setImageUrl(String url, ImageLoader imageLoader) {
        shouldAnimate = !imageLoader.isCached(url, 0, 0);
        super.setImageUrl(url, imageLoader);
    }
}

希望这能帮助到一些人!

我在尝试让这个程序按照预期工作时遇到了一些问题。我认为问题出在setImageUrl函数中判断图像是否被缓存时,你使用了0宽度和0高度来检查URL,从而得出图像未被缓存的结论。 - Teilmann

1
此外,是否有一种方法可以在不使用ImageLoader.get(..)的情况下设置网络图像视图的加载和错误位图?NetworkImageView具有这两个操作的方法:
mNetworkImageView.setErrorImageResId(R.drawable.errorImageResourceId);
mNetworkImageView.setDefaultImageResId(R.drawable.loadingImageResourceId);

0

这个版本的NetworkImageView在设置URL时会检查ImageLoader是否存在缓存中的图像并禁用动画。与@DalvikDroid的答案相比,它还尊重缓存的with/height/scalingType参数。

/**
 * NetworkImageView that fades-in the drawable upon complete download
 */
public class AnimatedNetworkImageView extends NetworkImageView {
    private static final int ANIM_DURATION = 500;
    private boolean shouldAnimate = true;

    public AnimatedNetworkImageView(Context context) {
        super(context);
        init();
    }

    public AnimatedNetworkImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public AnimatedNetworkImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        shouldAnimate = true; // animate by default. Only when {@link #determineIfAnimationIsNecessary} is called animation is dependent upon cache status
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        if (shouldAnimate) {
            // your animation. Here with ObjectAnimator for example
            ObjectAnimator.ofFloat(this, "alpha", 0, 1).setDuration(ANIM_DURATION).start();
        }
    }

    @Override
    public void setImageUrl(String url, ImageLoader imageLoader) {
        shouldAnimate = determineIfAnimationIsNecessary(url, imageLoader);
        super.setImageUrl(url, imageLoader);
    }

    /**
     * checks if for the given imgUrl and imageLoader the view should animate when a bitmap is set.
     * If this method is called before {@link NetworkImageView#setImageUrl(String, ImageLoader)} is called the view would not be animated if the image comes from the cache.
     *
     * @param imgUrl      the image url
     * @param imageLoader the image loader
     */
    public boolean determineIfAnimationIsNecessary(String imgUrl, ImageLoader imageLoader) {
        int width = getWidth();
        int height = getHeight();
        ScaleType scaleType = getScaleType();

        boolean wrapWidth = false, wrapHeight = false;
        if (getLayoutParams() != null) {
            wrapWidth = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT;
            wrapHeight = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
        }

        // Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens.
        int maxWidth = wrapWidth ? 0 : width;
        int maxHeight = wrapHeight ? 0 : height;

        return !imageLoader.isCached(imgUrl, maxWidth, maxHeight, scaleType);
    }
}

0
我建议使用Picasso库来加载图片或Glide。 如果需要加载图片和动画,请使用Picasso与此库:com.github.florent37:materialimageloading 类似这样的解决方案...
Picasso.with(this).load(bannerUrl).fit().centerCrop().into(bannerImage, new Callback() {

        @Override
        public void onSuccess() {
            MaterialImageLoading.animate(bannerImage).setDuration(2000).start();
        }

        @Override
        public void onError() {

        }
    });

如果你想为图像加载使用一些授权功能,可以像这样使用Glide..
        GlideUrl glideUrl = new GlideUrl(bannerUrl, new LazyHeaders.Builder()
            .addHeader("username", "heyyou")
            .addHeader("password", "heyyou")
            .build());
    Glide.with(this).load(bannerUrl)
            .centerCrop()
            .crossFade(5000)
            .into(bannerImage);

此外,Picasso用于身份验证的东西有拦截器,但我使用了Glide。也许这可以帮助解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接