NetworkImageView.java revision da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8
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.text.TextUtils; 20d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.util.AttributeSet; 21d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport android.widget.ImageView; 22d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 23d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodimport com.android.volley.toolbox.ImageLoader.ImageContainer; 24d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 25d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod/** 26d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Handles fetching an image from a URL as well as the life-cycle of the 27d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * associated request. 28d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 29d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbodpublic class NetworkImageView extends ImageView { 30d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** The URL of the network image to load */ 31d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private String mUrl; 32d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 33d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 34d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Resource ID of the image to be used as a placeholder until the network image is loaded. 35d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 36d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private int mDefaultImageId; 37d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 38d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 39d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Resource ID of the image to be used if the network response fails. 40d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 41d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private int mErrorImageId; 42d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 43d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** Local copy of the ImageLoader. */ 44d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private ImageLoader mImageLoader; 45d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 46da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton /** Current ImageContainer. (either in-flight or finished) */ 47da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton private ImageContainer mImageContainer; 48da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton 49d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public NetworkImageView(Context context) { 50d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod this(context, null); 51d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 52d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 53d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public NetworkImageView(Context context, AttributeSet attrs) { 54d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod this(context, attrs, 0); 55d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 56d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 57d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public NetworkImageView(Context context, AttributeSet attrs, int defStyle) { 58d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super(context, attrs, defStyle); 59d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 60d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 61d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 62d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Sets URL of the image that should be loaded into this view. Note that calling this will 63d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * immediately either set the cached image (if available) or the default image specified by 64d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * {@link NetworkImageView#setDefaultImageResId(int)} on the view. 65d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 66d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and 67d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * {@link NetworkImageView#setErrorImageResId(int)} should be called prior to calling 68d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * this function. 69d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * 70d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param url The URL that should be loaded into this ImageView. 71d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * @param imageLoader ImageLoader that will be used to make the request. 72d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 73d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void setImageUrl(String url, ImageLoader imageLoader) { 74d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mUrl = url; 75d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mImageLoader = imageLoader; 76d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // The URL has potentially changed. See if we need to load it. 77d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod loadImageIfNecessary(); 78d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 79d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 80d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 81d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Sets the default image resource ID to be used for this view until the attempt to load it 82d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * completes. 83d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 84d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void setDefaultImageResId(int defaultImage) { 85d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mDefaultImageId = defaultImage; 86d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 87d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 88d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 89d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Sets the error image resource ID to be used for this view in the event that the image 90d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * requested fails to load. 91d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 92d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod public void setErrorImageResId(int errorImage) { 93d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod mErrorImageId = errorImage; 94d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 95d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 96d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod /** 97d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod * Loads the image for the view if it isn't already loaded. 98d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod */ 99d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod private void loadImageIfNecessary() { 100d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod int width = getWidth(); 101d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod int height = getHeight(); 102d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 103d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if the view's bounds aren't known yet, hold off on loading the image. 104d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (width == 0 && height == 0) { 105d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return; 106d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 107d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 108d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if the URL to be loaded in this view is empty, cancel any old requests and clear the 109d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // currently loaded image. 110d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod if (TextUtils.isEmpty(mUrl)) { 111da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton if (mImageContainer != null) { 112da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton mImageContainer.cancelRequest(); 113da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton mImageContainer = null; 114d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 115da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton setImageBitmap(null); 116d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return; 117d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 118d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 119d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if there was an old request in this view, check if it needs to be canceled. 120da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { 121da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton if (mImageContainer.getRequestUrl().equals(mUrl)) { 122d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if the request is from the same URL, return. 123d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod return; 124d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } else { 125d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // if there is a pre-existing request, cancel it if it's fetching a different URL. 126da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton mImageContainer.cancelRequest(); 127d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setImageBitmap(null); 128d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 129d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 130d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 131d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // The pre-existing content of this view didn't match the current URL. Load the new image 132d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // from the network. 133d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageContainer newContainer = mImageLoader.get(mUrl, 134d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod ImageLoader.getImageListener(this, mDefaultImageId, mErrorImageId)); 135d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 136da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton // update the ImageContainer to be the new bitmap container. 137da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton mImageContainer = newContainer; 138d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 139d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 140d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 141d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 142d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super.onLayout(changed, left, top, right, bottom); 143d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod loadImageIfNecessary(); 144d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 145d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 146d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 147d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod protected void onDetachedFromWindow() { 148da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton if (mImageContainer != null) { 149d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // If the view was bound to an image request, cancel it and clear 150d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod // out the image from the view. 151da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton mImageContainer.cancelRequest(); 152d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod setImageBitmap(null); 153da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton // also clear out the container so we can reload the image if necessary. 154da703141dabd9cae3f71e15a4b9cbb79f6d5d4b8Evan Charlton mImageContainer = null; 155d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 156d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super.onDetachedFromWindow(); 157d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 158d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod 159d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod @Override 160d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod protected void drawableStateChanged() { 161d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod super.drawableStateChanged(); 162d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod invalidate(); 163d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod } 164d62a616ebca5bfa4f9ec5517208e13f2d501b69aAurash Mahbod} 165