Progress bar while loading image using Glide
Asked Answered
A

9

132

Can I load a spinner in placeholder with rotating animation until the image is loaded using Glide?

I am trying to do that using .placeholder(R.Drawable.spinner) no animation is coming up?

It would be great if somebody could help me out?

Thanks!

Anis answered 10/2, 2016 at 2:34 Comment(1)
#39263903 Please solve that issueCrossroad
C
290

Edit: This is super simple now with the CircularProgressDrawable

build.gradle

implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"

MyGlideModule.kt

@GlideModule
class MyGlideModule : AppGlideModule()

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)

  val circularProgressDrawable = CircularProgressDrawable(this)
  circularProgressDrawable.strokeWidth = 5f
  circularProgressDrawable.centerRadius = 30f
  circularProgressDrawable.start()

  GlideApp.with(applicationContext)
      .load("https://raw.githubusercontent.com/bumptech/glide/master/static/glide_logo.png")
      .placeholder(circularProgressDrawable)
      .into(a_main_image)
}

These are some other Glide snippets


Old answer: You could also create a normal ProgressBar, and then hide it on Glide's onResourceReady().

The method that will be called when the resource load has finished.


Example:

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final ImageView imageView = (ImageView) findViewById(R.id.img_glide);
    final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress);

    Glide.with(this)
            .load("https://raw.githubusercontent.com/bumptech/glide/master/static/glide_logo.png")
            .listener(new RequestListener<Drawable>() {
                @Override
                public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                    progressBar.setVisibility(View.GONE);
                    return false;
                }

                @Override
                public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                    progressBar.setVisibility(View.GONE);
                    return false;
                }
            })
            .into(imageView);
}

activity_main.xml (layout):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:visibility="visible" />

    <ImageView
        android:id="@+id/img_glide"
        android:layout_width="match_parent"
        android:layout_height="100dp" />

</RelativeLayout>

Result:

enter image description here

Cropeared answered 10/2, 2016 at 3:28 Comment(11)
The onException should do progressBar.setVisibility(View.GONE); too. :)Idocrase
it's good but it doesn't show value of progress. How to add it?Dancette
The GlideDrawable class cannot be resolved though I have imported the Glide library. Do I have to add any additional libraries in order to use GlideDrawable?Stricture
You're probably importing the RC1 version of Glide where they replaced GlideDrawable for Drawable. Try importing compile 'com.github.bumptech.glide:glide:3.8.0 instead for now.Cropeared
CircularProgressDrawable is currently deprecated. If you want to take full control of image which is being loaded, I suggest Mark Cheng's answer. developer.android.com/reference/android/support/v4/widget/…Parasitic
Thanks Marcos, CircularProgressDrawable is not deprecated, the support library usage is though. I've updated the answer to point to the androidx version. developer.android.com/reference/androidx/swiperefreshlayout/…Cropeared
won't the circular progress bar spin to infinity and take some memory in the background in the first solution since its never stopped ?Lonnielonny
I couldn't get the new solution to work, but the old one works perfectly fine.Lawrenson
@Cropeared This is good solution, but how to stop this progress on error?Cower
how to change it's color?Fremantle
Be careful with this solution. Whilst it does work, CircularProgressDrawable uses an update listener that overrides onAnimationUpdate, which continues to be repeatedly called each frame, even when it's not used. Glide won't stop that animator when it replaces the placeholder either (if it uses it at all), so you need to manage when to start/stop the drawable animation yourself.Prior
K
49

You can set progress value whatever you want with my GlideImageLoader.

I hope it will solve your question.

APP DEMO

I encapsulate Image loader with progress in GlideImageLoader.java & ProgressAppGlideModule .java

How to implement with 3 steps:

1. build.gradle

//Glide
implementation 'com.github.bumptech.glide:glide:4.4.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.4.0'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.4.0'

2. Clone GlideImageLoader.java & ProgressAppGlideModule.Java into Your Project

3. Simple Use anywhere

RequestOptions options = new RequestOptions()
                    .centerCrop()
                    .placeholder(R.drawable.placeholder)
                    .error(R.drawable.ic_pic_error)
                    .priority(Priority.HIGH);

new GlideImageLoader(YOUR.imageView,  
                     YOUR.progressBar).load(url,options);

Complete Java code for clone:

GlideImageLoader.java

public class GlideImageLoader {

    private ImageView mImageView;
    private ProgressBar mProgressBar;

    public GlideImageLoader(ImageView imageView, ProgressBar progressBar) {
        mImageView = imageView;
        mProgressBar = progressBar;
    }

    public void load(final String url, RequestOptions options) {
        if (url == null || options == null) return;

        onConnecting();

        //set Listener & start
        ProgressAppGlideModule.expect(url, new ProgressAppGlideModule.UIonProgressListener() {
            @Override
            public void onProgress(long bytesRead, long expectedLength) {
                if (mProgressBar != null) {
                    mProgressBar.setProgress((int) (100 * bytesRead / expectedLength));
                }
            }

            @Override
            public float getGranualityPercentage() {
                return 1.0f;
            }
        });
        //Get Image
        Glide.with(mImageView.getContext())
                .load(url)
                .transition(withCrossFade())
                .apply(options.skipMemoryCache(true))
                .listener(new RequestListener<Drawable>() {
                    @Override
                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                        ProgressAppGlideModule.forget(url);
                        onFinished();
                        return false;
                    }

                    @Override
                    public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                        ProgressAppGlideModule.forget(url);
                        onFinished();
                        return false;
                    }
                })
                .into(mImageView);
    }


    private void onConnecting() {
        if (mProgressBar != null) mProgressBar.setVisibility(View.VISIBLE);
    }

    private void onFinished() {
        if (mProgressBar != null && mImageView != null) {
            mProgressBar.setVisibility(View.GONE);
            mImageView.setVisibility(View.VISIBLE);
        }
    }
}

ProgressAppGlideModule.java

@GlideModule
public class ProgressAppGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {
        super.registerComponents(context, glide, registry);
        OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        Response response = chain.proceed(request);
                        ResponseProgressListener listener = new DispatchingProgressListener();
                        return response.newBuilder()
                                .body(new OkHttpProgressResponseBody(request.url(), response.body(), listener))
                                .build();
                    }
                })
                .build();
        registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
    }

    public static void forget(String url) {
        ProgressAppGlideModule.DispatchingProgressListener.forget(url);
    }
    public static void expect(String url, ProgressAppGlideModule.UIonProgressListener listener) {
        ProgressAppGlideModule.DispatchingProgressListener.expect(url, listener);
    }

    private interface ResponseProgressListener {
        void update(HttpUrl url, long bytesRead, long contentLength);
    }

    public interface UIonProgressListener {
        void onProgress(long bytesRead, long expectedLength);
        /**
         * Control how often the listener needs an update. 0% and 100% will always be dispatched.
         * @return in percentage (0.2 = call {@link #onProgress} around every 0.2 percent of progress)
         */
        float getGranualityPercentage();
    }

    private static class DispatchingProgressListener implements ProgressAppGlideModule.ResponseProgressListener {
        private static final Map<String, UIonProgressListener> LISTENERS = new HashMap<>();
        private static final Map<String, Long> PROGRESSES = new HashMap<>();

        private final Handler handler;

        DispatchingProgressListener() {
            this.handler = new Handler(Looper.getMainLooper());
        }

        static void forget(String url) {
            LISTENERS.remove(url);
            PROGRESSES.remove(url);
        }

        static void expect(String url, UIonProgressListener listener) {
            LISTENERS.put(url, listener);
        }

        @Override
        public void update(HttpUrl url, final long bytesRead, final long contentLength) {
            //System.out.printf("%s: %d/%d = %.2f%%%n", url, bytesRead, contentLength, (100f * bytesRead) / contentLength);
            String key = url.toString();
            final UIonProgressListener listener = LISTENERS.get(key);
            if (listener == null) {
                return;
            }
            if (contentLength <= bytesRead) {
                forget(key);
            }
            if (needsDispatch(key, bytesRead, contentLength, listener.getGranualityPercentage())) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onProgress(bytesRead, contentLength);
                    }
                });
            }
        }

        private boolean needsDispatch(String key, long current, long total, float granularity) {
            if (granularity == 0 || current == 0 || total == current) {
                return true;
            }
            float percent = 100f * current / total;
            long currentProgress = (long) (percent / granularity);
            Long lastProgress = PROGRESSES.get(key);
            if (lastProgress == null || currentProgress != lastProgress) {
                PROGRESSES.put(key, currentProgress);
                return true;
            } else {
                return false;
            }
        }
    }

    private static class OkHttpProgressResponseBody extends ResponseBody {
        private final HttpUrl url;
        private final ResponseBody responseBody;
        private final ResponseProgressListener progressListener;
        private BufferedSource bufferedSource;

        OkHttpProgressResponseBody(HttpUrl url, ResponseBody responseBody,
                                   ResponseProgressListener progressListener) {
            this.url = url;
            this.responseBody = responseBody;
            this.progressListener = progressListener;
        }

        @Override
        public MediaType contentType() {
            return responseBody.contentType();
        }

        @Override
        public long contentLength() {
            return responseBody.contentLength();
        }

        @Override
        public BufferedSource source() {
            if (bufferedSource == null) {
                bufferedSource = Okio.buffer(source(responseBody.source()));
            }
            return bufferedSource;
        }

        private Source source(Source source) {
            return new ForwardingSource(source) {
                long totalBytesRead = 0L;

                @Override
                public long read(Buffer sink, long byteCount) throws IOException {
                    long bytesRead = super.read(sink, byteCount);
                    long fullLength = responseBody.contentLength();
                    if (bytesRead == -1) { // this source is exhausted
                        totalBytesRead = fullLength;
                    } else {
                        totalBytesRead += bytesRead;
                    }
                    progressListener.update(url, totalBytesRead, fullLength);
                    return bytesRead;
                }
            };
        }
    }
}
Knighthead answered 26/1, 2018 at 2:8 Comment(11)
You are right, i originally want to leave the comments rather than post an answer. But it seems i don't have enough reputation to leave comments. I add the necessary code here to prevent the link invalid.Knighthead
progress percentage not showing on using progress bar , just showing loading bar thats itShluh
finally it worked , i had to make changes for progress bar with custom progress and background, ThanksShluh
@Quicklearner can you show me how to change the progress bar style? so it can looks like the demo image on above?Warta
@DarariNurAmali you want code to implement the same in the above images?Shluh
@Quicklearner Yes, I don't know how to implement progress bar like that. Mine still showing indeterminate progress. A github gist will be very helpful though. Thank you..Warta
@Quicklearnerthank you very much, I'm looking forward for that.Warta
Nice job, @MarkCheng . Found also your article on Medium :)Caitiff
@MarkCheng Not able to print progress. Progressbar hide when image gets load but its progress is not triggering any event. Please help.Rego
What should be for placeholder?Benefit
To implement progress bar like the answer above follow this: https://mcmap.net/q/126864/-how-to-create-a-circular-progressbar-in-android-which-rotates-on-itMononucleosis
T
18

I was writing an app in Kotlin and was having this exact problem, but, unfortunately, the accepted answer was in Java. So, I rewrote it in Kotlin.

Glide.with(context)
                .load("<Insert Your URL>")
                .listener(object : RequestListener<Drawable> {
                    override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
                        progressBar.visibility = View.GONE
                        return false
                    }

                    override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
                        progressBar.visibility = View.GONE
                        return false
                    }
                })
                .into(holder.imageView)
Tiller answered 17/5, 2018 at 18:50 Comment(0)
L
18

Yes we can show loader on ImageView using Glide RequestOptions.
1)Use below compile line in app level gradle file.

implementation 'com.github.bumptech.glide:glide:4.8.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

2)Add progress_animation.xml file in drawable

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/loader_test"
    android:pivotX="50%"
    android:pivotY="50%"/>

3)Add below loader_test.png image in drawable
enter image description here

4)Create RequestOption as below

public  RequestOptions options = new RequestOptions()
            .centerCrop()
            .placeholder(R.drawable.progress_animation)
            .error(R.drawable.user_image)
            .diskCacheStrategy(DiskCacheStrategy.ALL)
            .priority(Priority.HIGH)
            .dontAnimate()
            .dontTransform();

5)Finally use this to load image as below

Glide.with(this).load(//*Your image url*//).apply(options).into(image_view);
Lepton answered 11/4, 2019 at 12:30 Comment(1)
It isn't actually animating.Marc
L
13

Here is my complete solution with a circle loading progress. I think it is better to use requestOptionsattributes to achieve this.

CircularProgressDrawable circularProgressDrawable = new CircularProgressDrawable(context);
circularProgressDrawable.setStrokeWidth(5f);
circularProgressDrawable.setCenterRadius(30f);
circularProgressDrawable.start();

RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(circularProgressDrawable);
requestOptions.error(R.drawable.ic_image_not_found);
requestOptions.skipMemoryCache(true);
requestOptions.fitCenter();


glide.load(imagePath) //passing your url to load image.
        .load(imagePath)
        .apply(requestOptions) // here you have all options you need
        .transition(DrawableTransitionOptions.withCrossFade()) // when image (url) will be loaded by glide then this face in animation help to replace url image in the place of placeHolder (default) image.
        .listener(requestListener)
        .into(view); //pass imageView reference to appear the image.

And you got it.

Langtry answered 10/1, 2020 at 23:13 Comment(4)
what is requestListener?Daisydaitzman
It is optionnal. You can check the doc here : bumptech.github.io/glide/javadocs/4100/index.html?com/bumptech/…Langtry
This is kinda beautiful. Thank you so muchDevotee
Is there any option to load thumbnail first, but keep loading spinner running until load finishes?Walling
A
7

Another trick: Put your ImageView inside a RelativeLayout with a ProgressBar behind it. Then load the image via Glide or Picasso. It will do the job for you without any deep digging. You don't need to hide or show the progressbar anymore. It will be fully overlapped by the image.

<RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/_180sdp">
            <ProgressBar
                android:layout_centerInParent="true"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <ImageView
                android:id="@+id/iv_image"
                android:layout_width="match_parent"
                android:layout_height="@dimen/_180sdp"
                android:scaleType="centerCrop"/>
</RelativeLayout>
Additament answered 10/9, 2018 at 19:5 Comment(0)
K
4

Try using RequestOptions

RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(R.drawable.ic_placeholder);
requestOptions.error(R.drawable.ic_error);


Glide.with(MainActivity.this)
            .load(url)
            .apply(requestOptions)
            .into(imageview);

This is the one way you can implement loading image using with Glide.

Kunstlied answered 3/9, 2018 at 16:59 Comment(1)
but this will not add a progress bar as he wants in the question it will just load the imageCineaste
S
1

Step -1 create drawable as progress_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/loader_test"
    android:pivotX="50%"
    android:pivotY="50%"/>

step 2 download loader_test and save in drawable.

step 3 for animate loading

step 4 in case of error user can set error image in error

 Glide.with(this).load(url).apply(
         RequestOptions().placeholder(R.drawable.progress_animation).error((R.drawable.ic_account)).centerCrop()
                            .diskCacheStrategy(DiskCacheStrategy.ALL)
                            .priority(Priority.HIGH)
                            .transform(CenterCrop(), RoundedCorners(1000)))
                    .into(image_view)
Senn answered 11/2, 2021 at 19:1 Comment(0)
S
0

Hi Why not use material progress bar .

<com.google.android.material.progressindicator.CircularProgressIndicator
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Shammer answered 11/2, 2023 at 6:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.