Project: android-joedayz
Code Examples
/*
 * Copyright (C) 2010 Cyril Mottier (http://www.cyrilmottier.com) 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package greendroid.widget; 
 
import greendroid.image.ImageProcessor; 
import greendroid.image.ImageRequest; 
import greendroid.image.ImageRequest.ImageRequestCallback; 
import greendroid.util.Config; 
import greendroid.util.GDUtils; 
import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.BitmapFactory.Options; 
import android.graphics.drawable.Drawable; 
import android.os.Parcel; 
import android.os.Parcelable; 
import android.text.TextUtils; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.widget.ImageView; 
 
import com.cyrilmottier.android.greendroid.R; 
 
/**
 * <p> 
 * A {@link AsyncImageView} is a network-aware ImageView. It may display images 
 * from the web according to a URL. {@link AsyncImageView} takes care of loading 
 * asynchronously images on the Internet. It also caches images in an 
 * application-wide cache to prevent loading images several times. 
 * </p> 
 * <p> 
 * Clients may listen the {@link OnImageViewLoadListener} to be notified of the 
 * current image loading state. 
 * </p> 
 * <p> 
 * {@link AsyncImageView} may be extremely useful in ListView's row. To prevent 
 * your {@link AsyncImageView} from downloading while scrolling or flinging it 
 * is a good idea to pause it using {@link #setPaused(boolean)} method. Once the 
 * scrolling/flinging is over, <em>un-pause</em> your {@link AsyncImageView}s 
 * using <code>setPaused(false)</code> 
 * </p> 
 *  
 * @author Cyril Mottier 
 */
 
public class AsyncImageView extends ImageView implements ImageRequestCallback { 
 
    private static final String LOG_TAG = AsyncImageView.class.getSimpleName(); 
 
    /**
     * Clients may listen to {@link AsyncImageView} changes using a 
     * {@link OnImageViewLoadListener}. 
     *  
     * @author Cyril Mottier 
     */
 
    public static interface OnImageViewLoadListener { 
 
        /**
         * Called when the image started to load 
         *  
         * @param imageView The AsyncImageView that started loading 
         */
 
        void onLoadingStarted(AsyncImageView imageView); 
 
        /**
         * Called when the image ended to load that is when the image has been 
         * downloaded and is ready to be displayed on screen 
         *  
         * @param imageView The AsyncImageView that ended loading 
         */
 
        void onLoadingEnded(AsyncImageView imageView, Bitmap image); 
 
        /**
         * Called when the image loading failed 
         *  
         * @param imageView The AsyncImageView that failed to load 
         */
 
        void onLoadingFailed(AsyncImageView imageView, Throwable throwable); 
    } 
 
    private static final int IMAGE_SOURCE_UNKNOWN = -1
    private static final int IMAGE_SOURCE_RESOURCE = 0
    private static final int IMAGE_SOURCE_DRAWABLE = 1
    private static final int IMAGE_SOURCE_BITMAP = 2
 
    private int mImageSource; 
    private Bitmap mDefaultBitmap; 
    private Drawable mDefaultDrawable; 
    private int mDefaultResId; 
 
    private String mUrl; 
    private ImageRequest mRequest; 
    private boolean mPaused; 
 
    private Bitmap mBitmap; 
    private OnImageViewLoadListener mOnImageViewLoadListener; 
    private ImageProcessor mImageProcessor; 
    private BitmapFactory.Options mOptions; 
 
    public AsyncImageView(Context context) { 
        this(context, null); 
    } 
 
    public AsyncImageView(Context context, AttributeSet attrs) { 
        this(context, attrs, 0); 
    } 
 
    public AsyncImageView(Context context, AttributeSet attrs, int defStyle) { 
        super(context, attrs, defStyle); 
 
        initializeDefaultValues(); 
 
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AsyncImageView, defStyle, 0); 
 
        Drawable d = a.getDrawable(R.styleable.AsyncImageView_defaultSrc); 
        if (d != null) { 
            setDefaultImageDrawable(d); 
        } 
 
        final int inDensity = a.getInt(R.styleable.AsyncImageView_inDensity, -1); 
        if (inDensity != -1) { 
            setInDensity(inDensity); 
        } 
 
        setUrl(a.getString(R.styleable.AsyncImageView_url)); 
 
        a.recycle(); 
    } 
 
    private void initializeDefaultValues() { 
        mImageSource = IMAGE_SOURCE_UNKNOWN; 
        mPaused = false
    } 
 
    /**
     * Return true if this AsyncImageView is currently loading an image. 
     *  
     * @return true if this AsyncImageView is currently loading an image. 
     *         Otherwise it returns false. 
     */
 
    public boolean isLoading() { 
        return mRequest != null
    } 
 
    /**
     * Return true if the displayed image has been correctly loaded. 
     *  
     * @return true if this AsyncImageView succeed to load the image at the 
     *         given url. 
     */
 
    public boolean isLoaded() { 
        return mRequest == null && mBitmap != null
    } 
 
    /**
     * Pause this AsyncImageView preventing it from downloading the image. The 
     * download process will start back once setPaused(false) is called. 
     *  
     * @param paused 
     */
 
    public void setPaused(boolean paused) { 
        if (mPaused != paused) { 
            mPaused = paused; 
            if (!paused) { 
                reload(); 
            } 
        } 
    } 
 
    /**
     * Helper to {@link #setOptions(Options)} that simply sets the inDensity for 
     * loaded image. 
     *  
     * @param inDensity 
     * @see AsyncImageView#setOptions(Options) 
     */
 
    public void setInDensity(int inDensity) { 
        if (mOptions == null) { 
            mOptions = new BitmapFactory.Options(); 
            mOptions.inDither = true
            mOptions.inScaled = true
            mOptions.inTargetDensity = getContext().getResources().getDisplayMetrics().densityDpi; 
        } 
 
        mOptions.inDensity = inDensity; 
    } 
 
    /**
     * Assign an Options object to this {@link AsyncImageView}. Those options 
     * are used internally by the {@link AsyncImageView} when decoding the 
     * image. This may be used to prevent the default behavior that loads all 
     * images as mdpi density. 
     *  
     * @param options 
     */
 
    public void setOptions(BitmapFactory.Options options) { 
        mOptions = options; 
    } 
 
    /**
     * Reload the image pointed by the given URL 
     */
 
    public void reload() { 
        reload(false); 
    } 
 
    /**
     * Reload the image pointed by the given URL. You may want to force 
     * reloading by setting the force parameter to true. 
     *  
     * @param force if true the AsyncImageView won't look into the 
     *            application-wide cache. 
     */
 
    public void reload(boolean force) { 
        if (mRequest == null && mUrl != null) { 
 
            // Prior downloading the image ... let's look in a cache ! 
            // TODO cyril: This is a synchronous call ... make it asynchronous 
            mBitmap = null
            if (!force) { 
                mBitmap = GDUtils.getImageCache(getContext()).get(mUrl); 
            } 
 
            if (mBitmap != null) { 
                setImageBitmap(mBitmap); 
                return
            } 
 
            if (Config.GD_INFO_LOGS_ENABLED) { 
                Log.i(LOG_TAG, "Cache miss. Starting to load the image at the given URL"); 
            } 
 
            setDefaultImage(); 
            mRequest = new ImageRequest(mUrl, this, mImageProcessor, mOptions); 
            mRequest.load(getContext()); 
        } 
    } 
 
    /**
     * Force the loading to be stopped. 
     */
 
    public void stopLoading() { 
        if (mRequest != null) { 
            mRequest.cancel(); 
            mRequest = null
        } 
    } 
 
    /**
     * Register a callback to be invoked when an event occured for this 
     * AsyncImageView. 
     *  
     * @param listener The listener that will be notified 
     */
 
    public void setOnImageViewLoadListener(OnImageViewLoadListener listener) { 
        mOnImageViewLoadListener = listener; 
    } 
 
    /**
     * Set the url of the image that will be used as the content of this 
     * AsyncImageView. The given may be null in order to display the default 
     * image. Please note the url may be a local url. For instance, you can 
     * asynchronously load images from the disk memory is the url scheme is 
     * <code>file://</code> 
     *  
     * @param url The url of the image to set. Pass null to force the 
     *            AsyncImageView to display the default image 
     */
 
    public void setUrl(String url) { 
 
        // Check the url has changed 
        if (mBitmap != null && url != null && url.equals(mUrl)) { 
            return
        } 
 
        stopLoading(); 
        mUrl = url; 
 
        // Setting the url to an empty string force the displayed image to the 
        // default image 
        if (TextUtils.isEmpty(mUrl)) { 
            mBitmap = null
            setDefaultImage(); 
        } else { 
            if (!mPaused) { 
                reload(); 
            } else { 
                // We're paused: let's look in a synchronous and efficient cache 
                // prior using the default image. 
                mBitmap = GDUtils.getImageCache(getContext()).get(mUrl); 
                if (mBitmap != null) { 
                    setImageBitmap(mBitmap); 
                    return
                } else { 
                    setDefaultImage(); 
                } 
            } 
        } 
    } 
 
    /**
     * Set the default bitmap as the content of this AsyncImageView 
     *  
     * @param bitmap The bitmap to set 
     */
 
    public void setDefaultImageBitmap(Bitmap bitmap) { 
        mImageSource = IMAGE_SOURCE_BITMAP; 
        mDefaultBitmap = bitmap; 
        setDefaultImage(); 
    } 
 
    /**
     * Set the default drawable as the content of this AsyncImageView 
     *  
     * @param drawable The drawable to set 
     */
 
    public void setDefaultImageDrawable(Drawable drawable) { 
        mImageSource = IMAGE_SOURCE_DRAWABLE; 
        mDefaultDrawable = drawable; 
        setDefaultImage(); 
    } 
 
    /**
     * Set the default resource as the content of this AsyncImageView 
     *  
     * @param resId The resource identifier to set 
     */
 
    public void setDefaultImageResource(int resId) { 
        mImageSource = IMAGE_SOURCE_RESOURCE; 
        mDefaultResId = resId; 
        setDefaultImage(); 
    } 
 
    /**
     * Set an image processor to this AsyncImageView. An ImageProcessor may be 
     * used in order to work on the retrieved Bitmap prior displaying it on 
     * screen. 
     *  
     * @param imageProcessor The {@link ImageProcessor} to set 
     * @see ImageProcessor 
     */
 
    public void setImageProcessor(ImageProcessor imageProcessor) { 
        mImageProcessor = imageProcessor; 
    } 
 
    private void setDefaultImage() { 
        if (mBitmap == null) { 
            switch (mImageSource) { 
                case IMAGE_SOURCE_BITMAP: 
                    setImageBitmap(mDefaultBitmap); 
                    break
                case IMAGE_SOURCE_DRAWABLE: 
                    setImageDrawable(mDefaultDrawable); 
                    break
                case IMAGE_SOURCE_RESOURCE: 
                    setImageResource(mDefaultResId); 
                    break
                default
                    setImageDrawable(null); 
                    break
            } 
        } 
    } 
 
    static class SavedState extends BaseSavedState { 
        String url; 
 
        SavedState(Parcelable superState) { 
            super(superState); 
        } 
 
        private SavedState(Parcel in) { 
            super(in); 
            url = in.readString(); 
        } 
 
        @Override 
        public void writeToParcel(Parcel out, int flags) { 
            super.writeToParcel(out, flags); 
            out.writeString(url); 
        } 
 
        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { 
            public SavedState createFromParcel(Parcel in) { 
                return new SavedState(in); 
            } 
 
            public SavedState[] newArray(int size) { 
                return new SavedState[size]; 
            } 
        }; 
    } 
 
    @Override 
    public Parcelable onSaveInstanceState() { 
        Parcelable superState = super.onSaveInstanceState(); 
        SavedState ss = new SavedState(superState); 
 
        ss.url = mUrl; 
 
        return ss; 
    } 
 
    @Override 
    public void onRestoreInstanceState(Parcelable state) { 
        SavedState ss = (SavedState) state; 
        super.onRestoreInstanceState(ss.getSuperState()); 
 
        setUrl(ss.url); 
    } 
 
    public void onImageRequestStarted(ImageRequest request) { 
        if (mOnImageViewLoadListener != null) { 
            mOnImageViewLoadListener.onLoadingStarted(this); 
        } 
    } 
 
    public void onImageRequestFailed(ImageRequest request, Throwable throwable) { 
        mRequest = null
        if (mOnImageViewLoadListener != null) { 
            mOnImageViewLoadListener.onLoadingFailed(this, throwable); 
        } 
    } 
 
    public void onImageRequestEnded(ImageRequest request, Bitmap image) { 
        mBitmap = image; 
        setImageBitmap(image); 
        if (mOnImageViewLoadListener != null) { 
            mOnImageViewLoadListener.onLoadingEnded(this, image); 
        } 
        mRequest = null
    } 
 
    public void onImageRequestCancelled(ImageRequest request) { 
        mRequest = null
        if (mOnImageViewLoadListener != null) { 
            mOnImageViewLoadListener.onLoadingFailed(thisnull); 
        } 
    } 
}