bumptech / glide

An image loading and caching library for Android focused on smooth scrolling
https://bumptech.github.io/glide/
Other
34.66k stars 6.13k forks source link

How to add default animation when set asBitmap()? #605

Closed start141 closed 9 years ago

start141 commented 9 years ago

My Glide version is 3.6.1, load line is:

Glide.with(mActivity)
     .load(new File(item.getPath()))
     .asBitmap()
     .placeholder(R.drawable.default_picture)
     .centerCrop()
     .into(holder.image);

Sorry, I don't know how to add the default animation of Glide. I have tried use animate(), but I don't know how to build the default animation! Sorry!

MIkeeJY commented 9 years ago

You can use .animate(int) and give it an R.anim resource. the default animation is actually .crossFade() .

start141 commented 9 years ago

@MIkeeJY when use asBitmap(), you can't use crossFade().

MIkeeJY commented 9 years ago

why not try animate(R.anim.xx)?

start141 commented 9 years ago

because I want to use the default animation.

TWiStErRob commented 9 years ago

When you use asBitmap you tell Glide that you need a Bitmap object in your Target, which, as such, is just a bunch of pixels. crossFade works because the default loading transcodes to Drawable (instead of Bitmap) which can be replaced with a TransitionDrawable to do crossfade animation. Why do you need asBitmap?

You can try animating yourself by creating a custom target based on BitmapImageViewTarget (override setResource I think) or in a listener (start anim and return true).

start141 commented 9 years ago

Thanks for you reply!

Why do you need asBitmap?

Because in our App, we don't want to load GIF images(we want to load as static images). If I just want not to load gif, what should I do? And also with the default animation.

TWiStErRob commented 9 years ago

Yep, then you need asBitmap. Try the solutions I proposed, look around the sources of the classes I mentioned for guides.

Also keep in mind that .animate(R.anim.abc_fade_in) is pretty close to crossfade.

start141 commented 9 years ago

I have tried use custom animation, but it's not good yet! I will try more times. Thanks!

sjudd commented 9 years ago

You can't use the default animation because that requires a Drawable and you're loading a Bitmap. As @TWiStErRob suggested, you can use fade in. You can also use a RequestListener, convert your Bitmap into a Drawable and set a TransitionDrawable yourself. Doing so would be relatively straight forward.

vsahu1986 commented 9 years ago

I really like the way Picasso did for Animating Bitmap . I did the same and its seems very efficient and effective to me . I did tried TransitionDrawable and ObjectAnimator to Animate Alpha , but its not effective ,

you can try this

final public class FadingDrawable extends BitmapDrawable {
    // Only accessed from main thread.
    private static final float FADE_DURATION = 200; //ms
    private final float density;
    Drawable placeholder;
    long startTimeMillis;
    boolean animating;
    int alpha = 0xFF;

    FadingDrawable(Context context, Bitmap bitmap, Drawable placeholder) {
        super(context.getResources(), bitmap);

        this.density = context.getResources().getDisplayMetrics().density;

        this.placeholder = placeholder;
        animating = true;
        startTimeMillis = SystemClock.uptimeMillis();
    }

    /**
     * Create or update the drawable on the target {@link android.widget.ImageView} to display the supplied bitmap
     * image.
     */
    static public void setBitmap(ImageView target, Context context, Bitmap bitmap) {
        if (bitmap != null && !bitmap.isRecycled()) {
            Drawable placeholder = target.getDrawable();
            if (placeholder instanceof AnimationDrawable) {
                ((AnimationDrawable) placeholder).stop();
            }
            FadingDrawable drawable = new FadingDrawable(context, bitmap, placeholder);

            //this will avoid OverDraw
            //target.setBackgroundDrawable(null);
            //target.setBackgroundColor(0);

            target.setImageDrawable(drawable);

        }
    }

    /**
     * Create or update the drawable on the target {@link android.widget.ImageView} to display the supplied
     * placeholder image.
     */
    static void setPlaceholder(ImageView target, Drawable placeholderDrawable) {
        target.setImageDrawable(placeholderDrawable);
        if (target.getDrawable() instanceof AnimationDrawable) {
            ((AnimationDrawable) target.getDrawable()).start();
        }
    }

    @Override
    public void draw(Canvas canvas) {
        if (!animating) {
            super.draw(canvas);
        } else {
            float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION;
            if (normalized >= 1f) {
                animating = false;
                placeholder = null;
                super.draw(canvas);
            } else {
                if (placeholder != null) {
                    placeholder.draw(canvas);
                }

                int partialAlpha = (int) (alpha * normalized);
                super.setAlpha(partialAlpha);
                super.draw(canvas);
                super.setAlpha(alpha);
                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
                    invalidateSelf();
                }
            }
        }

    }

    @Override
    public void setAlpha(int alpha) {
        this.alpha = alpha;
        if (placeholder != null) {
            placeholder.setAlpha(alpha);
        }
        super.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        if (placeholder != null) {
            placeholder.setColorFilter(cf);
        }
        super.setColorFilter(cf);
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        if (placeholder != null) {
            placeholder.setBounds(bounds);
        }
        super.onBoundsChange(bounds);
    }
}
public class GlideImageView extends ImageView {
    public GlideImageView(Context context) {
        this(context, null);
    }

    public GlideImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Drawable placeholder = getDrawable();
        if (placeholder instanceof AnimationDrawable) {
            ((AnimationDrawable) placeholder).stop();
            Glide.clear(this);
        }
    }

    @Override
    public void setImageBitmap(Bitmap bitmap) {
        if (bitmap != null) FadingDrawable.setBitmap(this, getContext(), bitmap);
    }

    public void setImageBitmapWithoutAnimation(Bitmap bitmap) {
        super.setImageBitmap(bitmap);
    }
}

Usage

Glide.with(mContext).load(url).asBitmap().error(R.drawable.ic_error_icon).into(viewHolder.getNetworkImageView());

Hope this could help you.

start141 commented 9 years ago

Thanks for your code! @vsahu1986. Your Animation looks like the Picasso, I think. I will try it.

vsahu1986 commented 9 years ago

yup code credit to @JakeWharton Picasso ,

I already mentioned Picasso did this way ...

MIkeeJY commented 9 years ago

@vsahu1986. awesome,man <3

start141 commented 9 years ago

@MIkeeJY man <3 ?

vsahu1986 commented 9 years ago

Hi @TWiStErRob

Could you please give this Picasso Bitmap Animation a try , as it does not increase memory usage .

vipinhelloindia commented 9 years ago

This solution will always animate Image , whether it is from Memory Cache or Disk Cache

vsahu1986 commented 9 years ago

yup it will always fade your Bitmap , This is just a solution to animate bitmap , However it work well with RecyclerView , as you can use the cell to update the Image

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html

instead of calling notifyDataSetChanged()

vsahu1986 commented 9 years ago

This is what you can try to control animation for in memory or remote image

public class GlideImageView extends ImageView {
    public GlideImageView(Context context) {
        this(context, null);
    }

    public GlideImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Drawable placeholder = getDrawable();
        if (placeholder instanceof AnimationDrawable) {
            ((AnimationDrawable) placeholder).stop();
             Glide.clear(this);
        }
    }

    @Override
    public void setImageBitmap(Bitmap bitmap) {
        if (bitmap != null) FadingDrawable.setBitmap(this, getContext(), bitmap);
    }

    public void setImageBitmapWithoutAnimation(Bitmap bitmap) {
        super.setImageBitmap(bitmap);
    }

    public void loadUrl(String url) {
        Glide.with(getContext()).load(url).asBitmap().error(R.drawable.ic_error_icon).listener(new RequestListener<String, Bitmap>() {
            @Override
            public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {
                return false;
            }

            @Override
            public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
                if (isFromMemoryCache) {
                    setImageBitmapWithoutAnimation(resource);
                } else {
                    setImageBitmap(resource);
                }
                return true;
            }
        }).into(this);
    }
}

and

viewHolder.getNetworkImageView().loadUrl(url);

instead of

Glide.with(mContext).load(url).asBitmap().error(R.drawable.ic_error_icon).into(viewHolder.getNetworkImageView());
TWiStErRob commented 9 years ago

@ everyone this is close to what @sjudd was saying, except it uses everything built-in so you don't need to worry about not animating when memory cached or checking if there's a previous Drawable. Essentially 3-4 lines extra of code:

Glide
    .with(context)
    .load("https://media.giphy.com/media/4aBQ9oNjgEQ2k/giphy.gif")
    .asBitmap()
    .placeholder(android.R.drawable.gallery_thumb)
    .listener(new RequestListener<String, Bitmap>() {
        @Override public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {
            return false;
        }
        @Override public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
            ImageViewTarget imTarget = (ImageViewTarget)target;
            return new DrawableCrossFadeFactory<Drawable>()
                    .build(isFromMemoryCache, isFirstResource)
                    .animate(new BitmapDrawable(imTarget.getView().getResources(), resource), imTarget);
        }
    })
    .into(imageView)
;

If your .load() accepts other than String just change the listener types to the correct class.

TWiStErRob commented 8 years ago

Check out #840 for a more modular code and potential future built-in feature.

harolhiguera commented 8 years ago

Thanks TWiStErRob for your solution, it helped me to set a placeholder and an animation in a zoomable ViewPager with Bitmaps.