使用Glide加载图片时显示进度条

129

我能否在使用 Glide 时,在占位符中加载旋转动画的 spinner,直到图像加载完成?

我正在尝试使用 .placeholder(R.Drawable.spinner) 实现这一点,但没有出现动画效果?

如果有人能帮我解决问题那就太好了。

谢谢!


请解决这个问题:http://stackoverflow.com/questions/39263403/progress-bar-set-visiblity-using-grid-view-images-gallery - Danish Butt
9个回答

289
编辑:现在使用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)
}

这些是一些其他Glide代码片段
旧答案:您也可以创建一个普通的ProgressBar,然后在Glide的onResourceReady()方法中隐藏它。
引用:
当资源加载完成时将调用的方法。
例子:

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(布局):

<?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>

结果:

enter image description here


7
onException应该也做progressBar.setVisibility(View.GONE); :) - AndroidRuntimeException
1
很好,但它没有显示进度的价值。如何添加呢? - Han Whiteking
2
尽管我已经导入了Glide库,但无法解析GlideDrawable类。我需要添加其他库才能使用GlideDrawable吗? - XerXes
1
谢谢Marcos,CircularProgressDrawable并没有被弃用,只是支持库的使用被弃用了。我已经更新了答案,指向了androidx版本。https://developer.android.com/reference/androidx/swiperefreshlayout/widget/CircularProgressDrawable - Evin1_
1
在第一种解决方案中,由于它从未停止,圆形进度条不会无限旋转并占用后台内存吗? - Jean Raymond Daher
显示剩余6条评论

49

使用我的 GlideImageLoader,你可以自由设置进度条的值。

希望这能解决你的问题。

APP DEMO

我在 GlideImageLoader.java 和 ProgressAppGlideModule.java 中封装了带有进度条的图像加载器。

3步实现方法:

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. 克隆GlideImageLoader.java和ProgressAppGlideModule.java到您的项目中。

3. 简单地在任何地方使用它。

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

克隆的完整Java代码:

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

3
您说得对,我原本想留下评论而不是发表回答。但看起来我没有足够的声望来留下评论。 我在这里添加必要的代码以防止链接失效。 - Mark Cheng
@Quicklearner,你能向我展示如何更改进度条的样式吗?这样它就可以看起来像上面的演示图片了吗? - Darari Nur Amali
@DarariNurAmali,您需要实现上述图片中的代码吗? - Quick learner
@Quicklearner 是的,我不知道如何实现那样的进度条。我的仍然显示不确定的进度。一个 Github Gist 将非常有帮助。谢谢。 - Darari Nur Amali
@Quicklearner非常感谢,我期待着那个。 - Darari Nur Amali
显示剩余6条评论

18

我正在使用Kotlin编写一个应用程序,遇到了这个确切的问题,但不幸的是,被采纳的答案是用Java编写的。所以,我用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)

18

使用Glide RequestOptions,我们可以在ImageView上显示加载器。
1)在应用级别的gradle文件中使用以下编译行。

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

2)在可绘制的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%"/>

3)在drawable中添加以下loader_test.png图像
enter image description here

4)创建以下请求选项

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) 最后使用以下代码加载图像:

Glide.with(this).load(//*Your image url*//).apply(options).into(image_view);

1
它实际上没有动画。 - Phani Rithvij

13

这是我的完整解决方案,包含一个圆形加载进度条。我认为最好使用requestOptions属性来实现这个功能。

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.

你明白了。


requestListener是什么? - F.Mysir
1
这是可选的。您可以在此处查看文档:https://bumptech.github.io/glide/javadocs/4100/index.html?com/bumptech/glide/request/RequestListener.html - Zhar
这有点美丽。非常感谢。 - Valney Faria
有没有选项可以先加载“缩略图”,但保持加载旋转器运行,直到“加载”完成? - NeverEndingQueue

7

另一个技巧:将你的ImageView放在一个RelativeLayout中,并在其背后添加一个ProgressBar。然后通过Glide或Picasso加载图像。这样就可以为你完成工作,无需进行深入挖掘。你不需要再隐藏或显示进度条了。它会被图像完全覆盖。

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

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

这是使用Glide实现加载图片的一种方式。


3
但是这不会像他在问题中想要的那样添加进度条,它只会加载图像。 - Sattar

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%"/>

步骤2:下载loader_test并保存在drawable文件夹中。

步骤3:用于动画加载。

步骤4:如果出现错误,用户可以在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)

0

嗨,为什么不使用材料进度条。

<com.google.android.material.progressindicator.CircularProgressIndicator
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

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