Android 如何使用 Volley 库和 NetworkImageView 在 setImageUrl 上完成回调?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20292588/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:35:16  来源:igfitidea点击:

How to get finish callback on setImageUrl with Volley library and NetworkImageView?

androidimagecallbacklistenerandroid-volley

提问by Emil Adz

I'm trying out the Google's new Volley library and it's looking sharp and loads images quickly when I use this method setImageUrl:

我正在试用 Google 的新 Volley 库,当我使用这种方法时,它看起来很清晰并且可以快速加载图像setImageUrl

holder.image.setImageUrl(url, ImageCacheManager.getInstance().getImageLoader());

I want to add to it a call back/listener method that will fire up when loading is finished, so I can remove the progressBarview and show the image. It's an option that exists in Universal Image Loader and Picasso libraries, but for some reason, I can't find a way to do that in Volley, tried to Google different options but so far haven't found any reference.

我想向它添加一个回调/侦听器方法,该方法将在加载完成时启动,因此我可以删除progressBar视图并显示图像。这是通用图像加载器和毕加索库中存在的一个选项,但由于某种原因,我在 Volley 中找不到这样做的方法,尝试谷歌搜索不同的选项,但到目前为止还没有找到任何参考。

Does someone have a code sample to illustrate how it's done?

有人有代码示例来说明它是如何完成的吗?

回答by Yakiv Mospan

You can use this View instead of Google's View (I've copied sources from it and made some changes):

您可以使用此视图而不是 Google 的视图(我已经从中复制了源代码并进行了一些更改):

public class VolleyImageView extends ImageView {

    public interface ResponseObserver
    {
        public void onError();
        public void onSuccess();
    }

    private ResponseObserver mObserver;

    public void setResponseObserver(ResponseObserver observer) {
        mObserver = observer;
    }

    /**
     * The URL of the network image to load
     */
    private String mUrl;

    /**
     * Resource ID of the image to be used as a placeholder until the network image is loaded.
     */
    private int mDefaultImageId;

    /**
     * Resource ID of the image to be used if the network response fails.
     */
    private int mErrorImageId;

    /**
     * Local copy of the ImageLoader.
     */
    private ImageLoader mImageLoader;

    /**
     * Current ImageContainer. (either in-flight or finished)
     */
    private ImageContainer mImageContainer;

    public VolleyImageView(Context context) {
        this(context, null);
    }

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

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

    /**
     * Sets URL of the image that should be loaded into this view. Note that calling this will
     * immediately either set the cached image (if available) or the default image specified by
     * {@link VolleyImageView#setDefaultImageResId(int)} on the view.
     *
     * NOTE: If applicable, {@link VolleyImageView#setDefaultImageResId(int)} and {@link
     * VolleyImageView#setErrorImageResId(int)} should be called prior to calling this function.
     *
     * @param url         The URL that should be loaded into this ImageView.
     * @param imageLoader ImageLoader that will be used to make the request.
     */
    public void setImageUrl(String url, ImageLoader imageLoader) {
        mUrl = url;
        mImageLoader = imageLoader;
        // The URL has potentially changed. See if we need to load it.
        loadImageIfNecessary(false);
    }

    /**
     * Sets the default image resource ID to be used for this view until the attempt to load it
     * completes.
     */
    public void setDefaultImageResId(int defaultImage) {
        mDefaultImageId = defaultImage;
    }

    /**
     * Sets the error image resource ID to be used for this view in the event that the image
     * requested fails to load.
     */
    public void setErrorImageResId(int errorImage) {
        mErrorImageId = errorImage;
    }

    /**
     * Loads the image for the view if it isn't already loaded.
     *
     * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise.
     */
    private void loadImageIfNecessary(final boolean isInLayoutPass) {
        int width = getWidth();
        int height = getHeight();

        boolean isFullyWrapContent = getLayoutParams() != null
                && getLayoutParams().height == LayoutParams.WRAP_CONTENT
                && getLayoutParams().width == LayoutParams.WRAP_CONTENT;
        // if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content
        // view, hold off on loading the image.
        if (width == 0 && height == 0 && !isFullyWrapContent) {
            return;
        }

        // if the URL to be loaded in this view is empty, cancel any old requests and clear the
        // currently loaded image.
        if (TextUtils.isEmpty(mUrl)) {
            if (mImageContainer != null) {
                mImageContainer.cancelRequest();
                mImageContainer = null;
            }
            setDefaultImageOrNull();
            return;
        }

        // if there was an old request in this view, check if it needs to be canceled.
        if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
            if (mImageContainer.getRequestUrl().equals(mUrl)) {
                // if the request is from the same URL, return.
                return;
            } else {
                // if there is a pre-existing request, cancel it if it's fetching a different URL.
                mImageContainer.cancelRequest();
                setDefaultImageOrNull();
            }
        }

        // The pre-existing content of this view didn't match the current URL. Load the new image
        // from the network.
        ImageContainer newContainer = mImageLoader.get(mUrl,
                new ImageListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        if (mErrorImageId != 0) {
                            setImageResource(mErrorImageId);
                        }

                        if(mObserver!=null)
                        {
                            mObserver.onError();
                        }
                    }

                    @Override
                    public void onResponse(final ImageContainer response, boolean isImmediate) {
                        // If this was an immediate response that was delivered inside of a layout
                        // pass do not set the image immediately as it will trigger a requestLayout
                        // inside of a layout. Instead, defer setting the image by posting back to
                        // the main thread.
                        if (isImmediate && isInLayoutPass) {
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    onResponse(response, false);
                                }
                            });
                            return;
                        }

                        if (response.getBitmap() != null) {
                            setImageBitmap(response.getBitmap());
                        } else if (mDefaultImageId != 0) {
                            setImageResource(mDefaultImageId);
                        }

                        if(mObserver!=null)
                        {
                            mObserver.onSuccess();
                        }
                    }
                });

        // update the ImageContainer to be the new bitmap container.
        mImageContainer = newContainer;
    }

    private void setDefaultImageOrNull() {
        if (mDefaultImageId != 0) {
            setImageResource(mDefaultImageId);
        } else {
            setImageBitmap(null);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        loadImageIfNecessary(true);
    }

    @Override
    protected void onDetachedFromWindow() {
        if (mImageContainer != null) {
            // If the view was bound to an image request, cancel it and clear
            // out the image from the view.
            mImageContainer.cancelRequest();
            setImageBitmap(null);
            // also clear out the container so we can reload the image if necessary.
            mImageContainer = null;
        }
        super.onDetachedFromWindow();
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        invalidate();
    }
}

Usage example :

用法示例:

 //set observer to view
 holder.image.setResponseObserver(new VolleyImageView.ResponseObserver() {
     @Override
     public void onError() {

     }

     @Override
     public void onSuccess() {

     }
 });

//and then load image
holder.image.setImageUrl(url, ImageCacheManager.getInstance().getImageLoader());

回答by Rahul

I did it this way:-

我是这样做的:-

mImageLoader.get(url, new ImageLoader.ImageListener() {
    @Override
    public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
        if (response.getBitmap() != null)
            //some code
        else
            //some code
    }
    @Override
    public void onErrorResponse(VolleyError error) {
    }
});

回答by nerdinand

We used something like this:

我们使用了这样的东西:

imageView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View view, int i, int i2, int i3, int i4, int i5, int i6, int i7, int i8) {
        // the layout of the logo view changes at least twice: When it is added
        // to the parent and when the image has been loaded. We are only interested
        // in the second case and to find that case, we do this if statement

        if (imageView.getDrawable() != null) {
          doSomethingCoolHere();
        }

    }
});

It's not necessarily the most beautiful piece of code but it works (tm)

它不一定是最漂亮的一段代码,但它有效(tm)

回答by ChrisJ

Yet another approach, that relies on knowing the internals of NetworkImageView, is to subclass NetworkImageView to watch for the mErrorImageId getting applied.

另一种依赖于了解 NetworkImageView 内部结构的方法是将 NetworkImageView 子类化以观察 mErrorImageId 是否被应用。

public class ManagedNetworkImageView extends NetworkImageView{
    private int mErrorResId;

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

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

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

    @Override
    public void setErrorImageResId(int errorImage) {
        mErrorResId = errorImage;
        super.setErrorImageResId(errorImage);
    }

    @Override
    public void setImageResource(int resId) {
        if (resId == mErrorResId) {
            // TODO Handle the error here
        }
        super.setImageResource(resId);
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        // TODO Handle the success here
        super.setImageBitmap(bm);
    }
}

You'll also have to replace NetworkImageView with ManagedNetworkImageView in your layout files.

您还必须在布局文件中用 ManagedNetworkImageView 替换 NetworkImageView。

It's a bithacky, but does the job when NetworkImageView is already your chosen solution.

有点hacky,但是当 NetworkImageView 已经是您选择的解决方案时可以完成这项工作。

回答by Vladimir Salguero

Step 1: Declare imageLoader, (i have a MySocialMediaSingleton class for manage the Volley Request)

第 1 步:声明 imageLoader,(我有一个 MySocialMediaSingleton 类用于管理 Volley 请求)

ImageLoader imageLoader = MySocialMediaSingleton.getInstance(context).getImageLoader();

Step 2: use the callback for imageLoader

第 2 步:使用 imageLoader 的回调

imageLoader.get(url, new ImageLoader.ImageListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
             //an error ocurred
        }

        @Override
        public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
            if (response.getBitmap() != null) {
                //loadingView gone
            } else {
                //some code
            }
        }
    });

Step 3: Show the response in you imageView or NetworkImageView

第 3 步:在 imageView 或 NetworkImageView 中显示响应

holder.image.setImageUrl(ImageCacheManager.getInstance().getImageLoader(), imageLoader);

回答by Simulant

Listener<Bitmap> imageListener = new Listener<Bitmap>() {
    @Override
    public void onResponse(Bitmap response) {
        //This call back method is executed in the UI-Thread, when the loading is finished
        imageView.setImageBitmap(response); //example
    }
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
      //log your error
    }
};
//url, ListenerOnFinish, width, height, errorListener
ImageRequest getImageRequest = new ImageRequest(url, imageListener, 0, 0, null,errorListener);
requestQueue.add(getImageRequest);

回答by bkurzius

Another approach (similar to the code from @Simulant above) is to use use a regular ImageView in your xml and then make the image request using Volley.ImageRequest. If you use the Singleton pattern that is recommended by Googleit would look something like this:

另一种方法(类似于上面来自@Simulant 的代码)是在您的 xml 中使用常规 ImageView,然后使用 Volley.ImageRequest 发出图像请求。如果你使用谷歌推荐的单例模式,它看起来像这样:

ImageView mImageView = (ImageView) findViewById(R.id.myimageview);
RequestQueue requestQueue = MyVolleySingleton.getInstance(mContext).getRequestQueue();
ImageRequest mainImageRequest = new ImageRequest(myImageURL,
    new Response.Listener<Bitmap>() {
       @Override
       public void onResponse(Bitmap bitmap) {
          // set the image here
          mImageView.setImageBitmap(bitmap);
          // hide the spinner here
       }
    }, 0, 0, null, null);

 requestQueue.add(mainImageRequest);

By the way: Make sure that you use a regular ImageView instead of the NetworkImageView or the image will not show properly.

顺便说一句:确保您使用常规 ImageView 而不是 NetworkImageView 否则图像将无法正确显示。