Как получить завершающий ответ на setImageUrl с библиотекой Volley и NetworkImageView?

Я setImageUrl новую библиотеку Volley в Google, и она выглядит резкой и быстро загружает изображения, когда я использую этот метод setImageUrl :

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

Я хочу добавить к нему метод обратного вызова / прослушивателя, который будет срабатывать при завершении загрузки, поэтому я могу удалить представление progressBar и показать изображение. Это вариант, который существует в Universal Image Loader и библиотеках Picasso , но по какой-то причине я не могу найти способ сделать это в Volley , попробовал разные варианты Google, но до сих пор не нашел ссылок.

У некоторых есть образец кода, чтобы проиллюстрировать, как это делается?

Заранее спасибо.

Solutions Collecting From Web of "Как получить завершающий ответ на setImageUrl с библиотекой Volley и NetworkImageView?"

Вы можете использовать этот вид вместо Google View (я скопировал его источники и внесли некоторые изменения):

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

Пример использования:

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

Я сделал это так:

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

Мы использовали что-то вроде этого:

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

Это не обязательно самая красивая часть кода, но она работает ™

Еще один подход, основанный на знании внутренних элементов 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); } } 

Вам также придется заменить NetworkImageView на ManagedNetworkImageView в ваших файлах макетов.

Это немного взломано, но делает работу, когда NetworkImageView уже выбрано вами.

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

Другой подход (аналогичный коду из @Simulant выше) заключается в использовании регулярного ImageView в вашем xml, а затем сделать запрос изображения с помощью Volley.ImageRequest. Если вы используете шаблон Singleton, рекомендованный Google, он будет выглядеть примерно так:

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

Кстати: убедитесь, что вы используете обычный ImageView вместо NetworkImageView или изображение не будет отображаться должным образом.