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