Android Volley - how to animate image loading?
Asked Answered
R

6

13

any idea how to play a fade in animation when image loads? Now it just blinks into place. I am using NetworkImageView from the Volley toolkit.

Also, is there a way to set loading and error bitmaps on the network image view without using the ImageLoader.get( .. ) ?

Thanks!

//EDIT: Okay, thanks to you all, but if we want to be perfectionists, we should only animate if loading from disk cache, overriding setImageBitmap would case animation to go off even if pulled from memcache

what you want to do is add a boolean shouldAnimate to ImageListener.onResponse like this

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);
            }
        }
    };
}

this is a method that sets the bitmap, no matter where it is from, so you need to set it to false to every usage in ImageLoader except for

class BatchedImageRequest {

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

I've created a Gist for Copy & Paste usage - https://gist.github.com/ursusursus/5732521

Ruminant answered 27/5, 2013 at 17:53 Comment(3)
No need for the shouldAnimate Boolean since with your logic of "not animating when loading from the memory cache", in actuality shouldAnimate == !isImmediateNarcissus
Picasso did well to check whether the image is fetched from cache or network and them animate .Carce
mobilewebwizard.in/2015/02/… patch for Network Imageview as used in PicassoCarce
A
23

Example implementation of CommonsWare's answer can be found here: https://gist.github.com/benvd/5683818.

Using a TransitionDrawable does add an extra layer of overdraw. If you want to avoid that, perhaps using a ViewPropertyAnimator might help.

The gist of it is basically to have the following snippet in your 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);
Articulator answered 5/6, 2013 at 9:43 Comment(7)
Does the overdraw occur only during the actual transition? I would hope that TransitionDrawable would be sensible enough to only show one or the other at the end states, without overdraw, but I haven't experimented with it personally.Lawerencelawes
setAlpha(0f); setImageDrawable(new BitmapDrawable(getContext().getResources(), bm)); animate().alpha(1f).setDuration(FADE_IN_TIME_MS); for some reason, by my eye, the transition drawable seems to perform better, even though it adds a layer of overdraw, is it just me?Ruminant
by the way, this animates even if were pulling from memCache right?Ruminant
TransitionDrawable doesn't optimize for overdraw at the end of the transition, sadly.Articulator
Animations occur also when the image is pulled from the cache, yes.Articulator
+1 for supporting older versions of Android. @Vlasto if you want to avoid animating on cache load, you can put this code in your implementation of ImageListener inside a if (!isImmediate) statement.Narcissus
Having a look at the TransitionDrawable source code, it looks like the first drawable remains at all times unless you set crossfade enabled. With crossfade disabled (the default) you can have transparent areas in the drawable on top where you can see through to the drawable below. So, setCrossFadeEnabled(true) should get rid of your overdraw when the animation completes. android.googlesource.com/platform/frameworks/base/+/refs/heads/…Vinna
L
6

any idea how to play a fade in animation when image loads? Now it just blinks into place. I am using NetworkImageView from the Volley toolkit.

Off the cuff, create your own subclass of NetworkImageView and override setImageBitmap() to set up the alpha animation when the image is applied.

Also, is there a way to set loading and error bitmaps on the network image view without using the ImageLoader.get( .. ) ?

Call setImageBitmap() or setImageResource(), the same way you would with a regular ImageView.

Lawerencelawes answered 27/5, 2013 at 18:10 Comment(2)
Yea thats what I thought, I just wanted to know if there is something built in, as there is virtually no examples how to use the library other then few IO snippets.Ruminant
@Articulator you might want to make that an answerRuminant
W
5

This is my implementation of an animating NetworkImageView. It uses ObjectAnimator to fade in newly downloaded images, while cached images appear immediately. The implementation is pretty simple - only requiring you to override two NetworkImageView methods (setImageBitmap(Bitmap) and setImageUrl(String, ImageLoader). It uses the supplied ImageLoader to determine if the image is from cache.

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);
    }
}

Hope this helps someone!

Wrongly answered 3/5, 2014 at 1:49 Comment(1)
Im having some trouble getting this working as intended. I think the problem is that, when finding out if the image is cached or not, in the setImageUrl function, you check the url with 0 width and 0 height, and it concludes that the image is not cached.Vaso
A
1

Also, is there a way to set loading and error bitmaps on the network image view without using the ImageLoader.get( .. ) ?

NetworkImageView has methods for these two operations :

mNetworkImageView.setErrorImageResId(R.drawable.errorImageResourceId);
mNetworkImageView.setDefaultImageResId(R.drawable.loadingImageResourceId);
Arlenarlena answered 30/8, 2013 at 13:23 Comment(0)
R
0

i recommend to use Picasso lib for loading images or Glide. for loading images + animation use Picasso with this lib: com.github.florent37:materialimageloading solution like this..

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() {

        }
    });

if you maybe want some auth stuff for image loading use Glide like this..

        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);

in addition Picasso for auth stuff have interceptor things but i used Glide. maybe this helps..

Rattle answered 30/7, 2015 at 16:31 Comment(0)
V
0

This version of the NetworkImageView checks the ImageLoader upon url set if the image is in the cache and disables the animation. In contrast to @DalvikDroid answer this also respects the with/height/scalingType parameter for the cache.

/**
 * 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);
    }
}
Vengeance answered 14/12, 2015 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.