NetworkImageView.java revision d62a616ebca5bfa4f9ec5517208e13f2d501b69a
1d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod/** 2d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Copyright (C) 2013 The Android Open Source Project 3d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 4d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Licensed under the Apache License, Version 2.0 (the "License"); 5d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * you may not use this file except in compliance with the License. 6d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * You may obtain a copy of the License at 7d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 8d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * http://www.apache.org/licenses/LICENSE-2.0 9d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 10d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Unless required by applicable law or agreed to in writing, software 11d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * distributed under the License is distributed on an "AS IS" BASIS, 12d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * See the License for the specific language governing permissions and 14d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * limitations under the License. 15d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 16d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodpackage com.android.volley.toolbox; 17d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 18d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.content.Context; 19d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.graphics.Bitmap; 20d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.text.TextUtils; 21d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.util.AttributeSet; 22d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.widget.ImageView; 23d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 24d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport com.android.volley.toolbox.ImageLoader.ImageContainer; 25d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 26d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod/** 27d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Handles fetching an image from a URL as well as the life-cycle of the 28d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * associated request. 29d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 30d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodpublic class NetworkImageView extends ImageView { 31d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** The URL of the network image to load */ 32d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private String mUrl; 33d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 34d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 35d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Resource ID of the image to be used as a placeholder until the network image is loaded. 36d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 37d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private int mDefaultImageId; 38d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 39d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 40d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Resource ID of the image to be used if the network response fails. 41d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 42d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private int mErrorImageId; 43d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 44d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** Local copy of the ImageLoader. */ 45d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private ImageLoader mImageLoader; 46d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 47d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public NetworkImageView(Context context) { 48d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod this(context, null); 49d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 50d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 51d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public NetworkImageView(Context context, AttributeSet attrs) { 52d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod this(context, attrs, 0); 53d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 54d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 55d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public NetworkImageView(Context context, AttributeSet attrs, int defStyle) { 56d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super(context, attrs, defStyle); 57d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 58d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 59d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 60d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Sets URL of the image that should be loaded into this view. Note that calling this will 61d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * immediately either set the cached image (if available) or the default image specified by 62d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * {@link NetworkImageView#setDefaultImageResId(int)} on the view. 63d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 64d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and 65d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * {@link NetworkImageView#setErrorImageResId(int)} should be called prior to calling 66d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * this function. 67d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 68d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param url The URL that should be loaded into this ImageView. 69d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param imageLoader ImageLoader that will be used to make the request. 70d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 71d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void setImageUrl(String url, ImageLoader imageLoader) { 72d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mUrl = url; 73d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mImageLoader = imageLoader; 74d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // The URL has potentially changed. See if we need to load it. 75d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod loadImageIfNecessary(); 76d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 77d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 78d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 79d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Sets the default image resource ID to be used for this view until the attempt to load it 80d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * completes. 81d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 82d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void setDefaultImageResId(int defaultImage) { 83d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mDefaultImageId = defaultImage; 84d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 85d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 86d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 87d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Sets the error image resource ID to be used for this view in the event that the image 88d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * requested fails to load. 89d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 90d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void setErrorImageResId(int errorImage) { 91d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mErrorImageId = errorImage; 92d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 93d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 94d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 95d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Loads the image for the view if it isn't already loaded. 96d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 97d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private void loadImageIfNecessary() { 98d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod int width = getWidth(); 99d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod int height = getHeight(); 100d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 101d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if the view's bounds aren't known yet, hold off on loading the image. 102d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (width == 0 && height == 0) { 103d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return; 104d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 105d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 106d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if the URL to be loaded in this view is empty, cancel any old requests and clear the 107d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // currently loaded image. 108d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (TextUtils.isEmpty(mUrl)) { 109d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageContainer oldContainer = (ImageContainer) getTag(); 110d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (oldContainer != null) { 111d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod oldContainer.cancelRequest(); 112d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setImageBitmap(null); 113d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 114d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return; 115d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 116d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 117d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageContainer oldContainer = (ImageContainer) getTag(); 118d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if there was an old request in this view, check if it needs to be canceled. 119d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (oldContainer != null && oldContainer.getRequestUrl() != null) { 120d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (oldContainer.getRequestUrl().equals(mUrl)) { 121d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if the request is from the same URL, return. 122d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return; 123d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } else { 124d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if there is a pre-existing request, cancel it if it's fetching a different URL. 125d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod oldContainer.cancelRequest(); 126d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setImageBitmap(null); 127d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 128d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 129d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 130d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // The pre-existing content of this view didn't match the current URL. Load the new image 131d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // from the network. 132d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageContainer newContainer = mImageLoader.get(mUrl, 133d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageLoader.getImageListener(this, mDefaultImageId, mErrorImageId)); 134d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 135d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // update the tag to be the new bitmap container. 136d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setTag(newContainer); 137d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 138d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // look at the contents of the new container. if there is a bitmap, load it. 139d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod final Bitmap bitmap = newContainer.getBitmap(); 140d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (bitmap != null) { 141d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setImageBitmap(bitmap); 142d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 143d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 144d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 145d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 146d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 147d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super.onLayout(changed, left, top, right, bottom); 148d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod loadImageIfNecessary(); 149d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 150d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 151d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 152d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod protected void onDetachedFromWindow() { 153d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageContainer oldContainer = (ImageContainer) getTag(); 154d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (oldContainer != null) { 155d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // If the view was bound to an image request, cancel it and clear 156d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // out the image from the view. 157d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod oldContainer.cancelRequest(); 158d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setImageBitmap(null); 159d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // also clear out the tag so we can reload the image if necessary. 160d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setTag(null); 161d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 162d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super.onDetachedFromWindow(); 163d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 164d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 165d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 166d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod protected void drawableStateChanged() { 167d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super.drawableStateChanged(); 168d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod invalidate(); 169d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 170d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod} 171