package in.srain.cube.image;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import in.srain.cube.image.drawable.RecyclingBitmapDrawable;
/**
* Sub-class of ImageView which:
* <ul>
* <li>
* automatically notifies the Drawable when it is being displayed.
* </ul>
* <p/>
* Part of the code is taken from the Android best practice of displaying Bitmaps
* <p/>
* <a href="http://developer.android.com/training/displaying-bitmaps/index.html">Displaying Bitmaps Efficiently</a>.
*/
public class CubeImageView extends ImageView {
private String mUrl = "";
private int mSpecifiedWidth = 0;
private int mSpecifiedHeight = 0;
private ImageLoader mImageLoader;
private ImageReuseInfo mImageReuseInfo;
private ImageTask mImageTask;
private boolean mClearWhenDetached = true;
public CubeImageView(Context context) {
super(context);
}
public CubeImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setClearDrawableWhenDetached(boolean clearWhenDetached) {
mClearWhenDetached = clearWhenDetached;
}
public void clearDrawable() {
setImageDrawable(null);
if (null != mImageTask && null != mImageLoader) {
mImageLoader.detachImageViewFromImageTask(mImageTask, this);
clearLoadTask();
}
}
/**
* @see android.widget.ImageView#onDetachedFromWindow()
*/
@Override
protected void onDetachedFromWindow() {
// This has been detached from Window, so clear the drawable
// If this view is recycled ???
if (mClearWhenDetached) {
clearDrawable();
}
super.onDetachedFromWindow();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mClearWhenDetached) {
tryLoadImage();
}
}
/**
* @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)
*/
@Override
public void setImageDrawable(Drawable drawable) {
// Keep hold of previous Drawable
final Drawable previousDrawable = getDrawable();
// Call super to set new Drawable
super.setImageDrawable(drawable);
// Notify new Drawable that it is being displayed
notifyDrawable(drawable, true);
// Notify old Drawable so it is no longer being displayed
notifyDrawable(previousDrawable, false);
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
clearLoadTask();
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
clearLoadTask();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
clearLoadTask();
}
/**
* Notifies the drawable that it's displayed state has changed.
*
* @param drawable
* @param isDisplayed
*/
private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
if (drawable instanceof RecyclingBitmapDrawable) {
// The drawable is a CountingBitmapDrawable, so notify it
((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
} else if (drawable instanceof LayerDrawable) {
// The drawable is a LayerDrawable, so recurse on each layer
LayerDrawable layerDrawable = (LayerDrawable) drawable;
for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
}
}
}
public void onLoadFinish() {
}
private void clearLoadTask() {
mImageTask = null;
}
public void loadImage(ImageLoader imageLoader, String url) {
loadImage(imageLoader, url, 0, 0, null);
}
public void loadImage(ImageLoader imageLoader, String url, ImageReuseInfo imageReuseInfo) {
loadImage(imageLoader, url, 0, 0, imageReuseInfo);
}
public void loadImage(ImageLoader imageLoader, String url, int specifiedSize) {
loadImage(imageLoader, url, specifiedSize, specifiedSize, null);
}
public void loadImage(ImageLoader imageLoader, String url, int specifiedSize, ImageReuseInfo imageReuseInfo) {
loadImage(imageLoader, url, specifiedSize, specifiedSize, imageReuseInfo);
}
public void loadImage(ImageLoader imageLoader, String url, int specifiedWidth, int specifieHeight) {
loadImage(imageLoader, url, specifiedWidth, specifieHeight, null);
}
public void loadImage(ImageLoader imageLoader, String url, int specifiedWidth, int specifieHeight, ImageReuseInfo imageReuseInfo) {
mImageLoader = imageLoader;
mUrl = url;
mSpecifiedWidth = specifiedWidth;
mSpecifiedHeight = specifieHeight;
mImageReuseInfo = imageReuseInfo;
tryLoadImage();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
tryLoadImage();
}
private void tryLoadImage() {
if (TextUtils.isEmpty(mUrl)) {
return;
}
int width = getWidth();
int height = getHeight();
ViewGroup.LayoutParams lyp = getLayoutParams();
boolean isFullyWrapContent = lyp != null && lyp.height == LayoutParams.WRAP_CONTENT && lyp.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 (mSpecifiedWidth != 0) {
width = mSpecifiedWidth;
}
if (mSpecifiedHeight != 0) {
height = mSpecifiedHeight;
}
// 1. Check the previous ImageTask related to this ImageView
if (null != mImageTask) {
// duplicated ImageTask, return directly.
if (mImageTask.isLoadingThisUrl(mUrl)) {
return;
}
// ImageView is reused, detach it from the related ImageViews of the previous ImageTask.
else {
mImageLoader.detachImageViewFromImageTask(mImageTask, this);
}
}
// 2. Let the ImageView hold this ImageTask. When ImageView is reused next time, check it in step 1.
ImageTask imageTask = mImageLoader.createImageTask(mUrl, width, height, mImageReuseInfo);
mImageTask = imageTask;
// 3. Query cache, if hit, return at once.
boolean hitCache = mImageLoader.queryCache(imageTask, this);
if (hitCache) {
return;
} else {
mImageLoader.addImageTask(mImageTask, this);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}