[Android] Improve images handling (#109)

* Fixed the inSampleSize calculations based on total pixels (double inSampleSize size -> 4x less pixels)

* Fixed the inSampleSize calculations when only hight or widths is requested.
Added rotation and scaling while processing images.
Added keepAspectRatio property for further flexibility and accuracy.

* Fixed the ExifInterface creation based on the API level.

* Handle file not found in the native images handling.

* chore: apply PR comments
This commit is contained in:
Dimitar Tachev
2018-02-28 14:00:43 +02:00
committed by Alexander Djenkov
parent bddb2e911f
commit 9d2095cdad
5 changed files with 204 additions and 83 deletions

View File

@@ -261,7 +261,7 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
Fetcher fetcher = Fetcher.getInstance(context); Fetcher fetcher = Fetcher.getInstance(context);
// TODO: Implement option to pass load-mode like in ImageView class. // TODO: Implement option to pass load-mode like in ImageView class.
boolean loadAsync = backgroundImageUri.startsWith("http"); boolean loadAsync = backgroundImageUri.startsWith("http");
fetcher.loadImage(backgroundImageUri, this, 0, 0, true, loadAsync, null); fetcher.loadImage(backgroundImageUri, this, 0, 0, false, true, loadAsync, null);
} }
} }

View File

@@ -19,10 +19,13 @@ package org.nativescript.widgets.image;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import android.util.TypedValue;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@@ -31,7 +34,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@@ -156,7 +158,7 @@ public class Fetcher extends Worker {
* @param data The data to load the bitmap, in this case, a regular http URL * @param data The data to load the bitmap, in this case, a regular http URL
* @return The downloaded and resized bitmap * @return The downloaded and resized bitmap
*/ */
private Bitmap processHttp(String data, int decodeWidth, int decodeHeight) { private Bitmap processHttp(String data, int decodeWidth, int decodeHeight, boolean keepAspectRatio) {
final String key = Cache.hashKeyForDisk(data); final String key = Cache.hashKeyForDisk(data);
FileDescriptor fileDescriptor = null; FileDescriptor fileDescriptor = null;
FileInputStream fileInputStream = null; FileInputStream fileInputStream = null;
@@ -179,8 +181,7 @@ public class Fetcher extends Worker {
} }
DiskLruCache.Editor editor = mHttpDiskCache.edit(key); DiskLruCache.Editor editor = mHttpDiskCache.edit(key);
if (editor != null) { if (editor != null) {
if (downloadUrlToStream(data, if (downloadUrlToStream(data, editor.newOutputStream(DISK_CACHE_INDEX))) {
editor.newOutputStream(DISK_CACHE_INDEX))) {
editor.commit(); editor.commit();
} else { } else {
editor.abort(); editor.abort();
@@ -189,8 +190,7 @@ public class Fetcher extends Worker {
snapshot = mHttpDiskCache.get(key); snapshot = mHttpDiskCache.get(key);
} }
if (snapshot != null) { if (snapshot != null) {
fileInputStream = fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
(FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
fileDescriptor = fileInputStream.getFD(); fileDescriptor = fileInputStream.getFD();
} }
} catch (IOException e) { } catch (IOException e) {
@@ -210,8 +210,8 @@ public class Fetcher extends Worker {
Bitmap bitmap = null; Bitmap bitmap = null;
if (fileDescriptor != null) { if (fileDescriptor != null) {
bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, decodeWidth, bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, decodeWidth, decodeHeight, keepAspectRatio,
decodeHeight, getCache()); getCache());
} }
if (fileInputStream != null) { if (fileInputStream != null) {
try { try {
@@ -222,14 +222,15 @@ public class Fetcher extends Worker {
return bitmap; return bitmap;
} }
private Bitmap processHttpNoCache(String data, int decodeWidth, int decodeHeight) { private Bitmap processHttpNoCache(String data, int decodeWidth, int decodeHeight, boolean keepAspectRatio) {
ByteArrayOutputStreamInternal outputStream = null; ByteArrayOutputStreamInternal outputStream = null;
Bitmap bitmap = null; Bitmap bitmap = null;
try { try {
outputStream = new ByteArrayOutputStreamInternal(); outputStream = new ByteArrayOutputStreamInternal();
if (downloadUrlToStream(data, outputStream)) { if (downloadUrlToStream(data, outputStream)) {
bitmap = decodeSampledBitmapFromByteArray(outputStream.getBuffer(), decodeWidth, decodeHeight, getCache()); bitmap = decodeSampledBitmapFromByteArray(outputStream.getBuffer(), decodeWidth, decodeHeight,
keepAspectRatio, getCache());
} }
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Log.e(TAG, "processHttpNoCache - " + e); Log.e(TAG, "processHttpNoCache - " + e);
@@ -246,28 +247,30 @@ public class Fetcher extends Worker {
} }
@Override @Override
protected Bitmap processBitmap(String uri, int decodeWidth, int decodeHeight, boolean useCache) { protected Bitmap processBitmap(String uri, int decodeWidth, int decodeHeight, boolean keepAspectRatio,
boolean useCache) {
if (debuggable > 0) { if (debuggable > 0) {
Log.v(TAG, "process: " + uri); Log.v(TAG, "process: " + uri);
} }
if (uri.startsWith(FILE_PREFIX)) { if (uri.startsWith(FILE_PREFIX)) {
String filename = uri.substring(FILE_PREFIX.length()); String filename = uri.substring(FILE_PREFIX.length());
return decodeSampledBitmapFromFile(filename, decodeWidth, decodeHeight, getCache()); return decodeSampledBitmapFromFile(filename, decodeWidth, decodeHeight, keepAspectRatio, getCache());
} else if (uri.startsWith(RESOURCE_PREFIX)) { } else if (uri.startsWith(RESOURCE_PREFIX)) {
String resPath = uri.substring(RESOURCE_PREFIX.length()); String resPath = uri.substring(RESOURCE_PREFIX.length());
int resId = mResources.getIdentifier(resPath, "drawable", mPackageName); int resId = mResources.getIdentifier(resPath, "drawable", mPackageName);
if (resId > 0) { if (resId > 0) {
return decodeSampledBitmapFromResource(mResources, resId, decodeWidth, decodeHeight, getCache()); return decodeSampledBitmapFromResource(mResources, resId, decodeWidth, decodeHeight, keepAspectRatio,
getCache());
} else { } else {
Log.v(TAG, "Missing Image with resourceID: " + uri); Log.v(TAG, "Missing Image with resourceID: " + uri);
return null; return null;
} }
} else { } else {
if (useCache && mHttpDiskCache != null) { if (useCache && mHttpDiskCache != null) {
return processHttp(uri, decodeWidth, decodeHeight); return processHttp(uri, decodeWidth, decodeHeight, keepAspectRatio);
} else { } else {
return processHttpNoCache(uri, decodeWidth, decodeHeight); return processHttpNoCache(uri, decodeWidth, decodeHeight, keepAspectRatio);
} }
} }
} }
@@ -342,8 +345,8 @@ public class Fetcher extends Worker {
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
* that are equal to or greater than the requested width and height * that are equal to or greater than the requested width and height
*/ */
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight,
int reqWidth, int reqHeight, Cache cache) { boolean keepAspectRatio, Cache cache) {
// BEGIN_INCLUDE (read_bitmap_dimensions) // BEGIN_INCLUDE (read_bitmap_dimensions)
// First decode with inJustDecodeBounds=true to check dimensions // First decode with inJustDecodeBounds=true to check dimensions
@@ -351,14 +354,7 @@ public class Fetcher extends Worker {
options.inJustDecodeBounds = true; options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options); BitmapFactory.decodeResource(res, resId, options);
// If requested width/height were not specified - decode in full size. options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
if (reqWidth > 0 && reqHeight > 0) {
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
}
else {
options.inSampleSize = 1;
}
// END_INCLUDE (read_bitmap_dimensions) // END_INCLUDE (read_bitmap_dimensions)
@@ -369,7 +365,74 @@ public class Fetcher extends Worker {
// Decode bitmap with inSampleSize set // Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options); Bitmap bitmap = null;
InputStream is = null;
try {
final TypedValue value = new TypedValue();
is = res.openRawResource(resId, value);
bitmap = BitmapFactory.decodeResourceStream(res, value, is, null, options);
} catch (Exception e) {
/* do nothing.
If the exception happened on open, bm will be null.
If it happened on close, bm is still valid.
*/
}
if (bitmap == null && options != null && options.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
ExifInterface ei = getExifInterface(is);
return scaleAndRotateBitmap(bitmap, ei, reqWidth, reqHeight, keepAspectRatio);
}
@TargetApi(Build.VERSION_CODES.N)
private static ExifInterface getExifInterface(InputStream is) {
ExifInterface ei = null;
try {
if (Utils.hasN()) {
ei = new ExifInterface(is);
}
} catch (final Exception e) {
Log.e(TAG, "Error in reading bitmap - " + e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
return ei;
}
@TargetApi(Build.VERSION_CODES.N)
private static ExifInterface getExifInterface(FileDescriptor fd) {
ExifInterface ei = null;
try {
if (Utils.hasN()) {
ei = new ExifInterface(fd);
}
} catch (final Exception e) {
Log.e(TAG, "Error in reading bitmap - " + e);
}
return ei;
}
private static ExifInterface getExifInterface(String fileName) {
ExifInterface ei = null;
try {
ei = new ExifInterface(fileName);
} catch (final Exception e) {
Log.e(TAG, "Error in reading bitmap - " + e);
}
return ei;
} }
/** /**
@@ -382,22 +445,15 @@ public class Fetcher extends Worker {
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
* that are equal to or greater than the requested width and height * that are equal to or greater than the requested width and height
*/ */
public static Bitmap decodeSampledBitmapFromFile(String filename, public static Bitmap decodeSampledBitmapFromFile(String fileName, int reqWidth, int reqHeight,
int reqWidth, int reqHeight, Cache cache) { boolean keepAspectRatio, Cache cache) {
// First decode with inJustDecodeBounds=true to check dimensions // First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options(); final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options); BitmapFactory.decodeFile(fileName, options);
// If requested width/height were not specified - decode in full size. options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
if (reqWidth > 0 && reqHeight > 0) {
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
}
else {
options.inSampleSize = 1;
}
// If we're running on Honeycomb or newer, try to use inBitmap // If we're running on Honeycomb or newer, try to use inBitmap
if (Utils.hasHoneycomb()) { if (Utils.hasHoneycomb()) {
@@ -406,7 +462,68 @@ public class Fetcher extends Worker {
// Decode bitmap with inSampleSize set // Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filename, options);
final Bitmap bitmap = BitmapFactory.decodeFile(fileName, options);
ExifInterface ei = getExifInterface(fileName);
return scaleAndRotateBitmap(bitmap, ei, reqWidth, reqHeight, keepAspectRatio);
}
private static Bitmap scaleAndRotateBitmap(Bitmap bitmap, ExifInterface ei, int reqWidth, int reqHeight,
boolean keepAspectRatio) {
if (bitmap == null) {
return null;
}
int sourceWidth = bitmap.getWidth();
int sourceHeight = bitmap.getHeight();
reqWidth = reqWidth > 0 ? reqWidth : sourceWidth;
reqHeight = reqHeight > 0 ? reqHeight : sourceHeight;
// scale
if (reqWidth != sourceWidth || reqHeight != sourceHeight) {
if (keepAspectRatio) {
double widthCoef = (double) sourceWidth / (double) reqWidth;
double heightCoef = (double) sourceHeight / (double) reqHeight;
double aspectCoef = widthCoef > heightCoef ? widthCoef : heightCoef;
reqWidth = (int) Math.floor(sourceWidth / aspectCoef);
reqHeight = (int) Math.floor(sourceHeight / aspectCoef);
}
bitmap = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, true);
}
// rotate
if (ei != null) {
final Matrix matrix = new Matrix();
final int rotationAngle = calculateRotationAngle(ei);
if (rotationAngle != 0) {
matrix.postRotate(rotationAngle);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
}
return bitmap;
}
private static int calculateRotationAngle(ExifInterface ei) {
int rotationAngle = 0;
final int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotationAngle = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotationAngle = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotationAngle = 270;
break;
}
return rotationAngle;
} }
/** /**
@@ -419,22 +536,15 @@ public class Fetcher extends Worker {
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
* that are equal to or greater than the requested width and height * that are equal to or greater than the requested width and height
*/ */
public static Bitmap decodeSampledBitmapFromDescriptor( public static Bitmap decodeSampledBitmapFromDescriptor(FileDescriptor fileDescriptor, int reqWidth, int reqHeight,
FileDescriptor fileDescriptor, int reqWidth, int reqHeight, Cache cache) { boolean keepAspectRatio, Cache cache) {
// First decode with inJustDecodeBounds=true to check dimensions // First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options(); final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
// If requested width/height were not specified - decode in full size. options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
if (reqWidth > 0 && reqHeight > 0) {
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
}
else {
options.inSampleSize = 1;
}
// Decode bitmap with inSampleSize set // Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
@@ -448,32 +558,27 @@ public class Fetcher extends Worker {
try { try {
// This can throw an error on a corrupted image when using an inBitmap // This can throw an error on a corrupted image when using an inBitmap
results = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); results = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
} } catch (Exception e) {
catch (Exception e) {
// clear the inBitmap and try again // clear the inBitmap and try again
options.inBitmap = null; options.inBitmap = null;
results = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); results = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
// If image is broken, rather than an issue with the inBitmap, we will get a NULL out in this case... // If image is broken, rather than an issue with the inBitmap, we will get a NULL out in this case...
} }
return results;
ExifInterface ei = getExifInterface(fileDescriptor);
return scaleAndRotateBitmap(results, ei, reqWidth, reqHeight, keepAspectRatio);
} }
public static Bitmap decodeSampledBitmapFromByteArray( public static Bitmap decodeSampledBitmapFromByteArray(byte[] buffer, int reqWidth, int reqHeight,
byte[] buffer, int reqWidth, int reqHeight, Cache cache) { boolean keepAspectRatio, Cache cache) {
// First decode with inJustDecodeBounds=true to check dimensions // First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options(); final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options); BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
// If requested width/height were not specified - decode in full size. options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
if (reqWidth > 0 && reqHeight > 0) {
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
}
else {
options.inSampleSize = 1;
}
// Decode bitmap with inSampleSize set // Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
@@ -483,7 +588,12 @@ public class Fetcher extends Worker {
addInBitmapOptions(options, cache); addInBitmapOptions(options, cache);
} }
return BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options); final Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
InputStream is = new ByteArrayInputStream(buffer);
ExifInterface ei = getExifInterface(is);
return scaleAndRotateBitmap(bitmap, ei, reqWidth, reqHeight, keepAspectRatio);
} }
/** /**
@@ -492,20 +602,20 @@ public class Fetcher extends Worker {
* the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
* having a width and height equal to or larger than the requested width and height. * having a width and height equal to or larger than the requested width and height.
* *
* @param options An options object with out* params already populated (run through a decode* * @param imageWidth The original width of the resulting bitmap
* method with inJustDecodeBounds==true * @param imageHeight The original height of the resulting bitmap
* @param reqWidth The requested width of the resulting bitmap * @param reqWidth The requested width of the resulting bitmap
* @param reqHeight The requested height of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap
* @return The value to be used for inSampleSize * @return The value to be used for inSampleSize
*/ */
public static int calculateInSampleSize(BitmapFactory.Options options, public static int calculateInSampleSize(int imageWidth, int imageHeight, int reqWidth, int reqHeight) {
int reqWidth, int reqHeight) {
// BEGIN_INCLUDE (calculate_sample_size) // BEGIN_INCLUDE (calculate_sample_size)
// Raw height and width of image // Raw height and width of image
final int height = options.outHeight; final int height = imageHeight;
final int width = options.outWidth; final int width = imageWidth;
reqWidth = reqWidth > 0 ? reqWidth : width;
reqHeight = reqHeight > 0 ? reqHeight : height;
int inSampleSize = 1; int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) { if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2; final int halfHeight = height / 2;
@@ -513,8 +623,7 @@ public class Fetcher extends Worker {
// Calculate the largest inSampleSize value that is a power of 2 and keeps both // Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width. // height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2; inSampleSize *= 2;
} }
@@ -524,14 +633,14 @@ public class Fetcher extends Worker {
// end up being too large to fit comfortably in memory, so we should // end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize). // be more aggressive with sample down the image (=larger inSampleSize).
long totalPixels = width * height / inSampleSize; long totalPixels = (width / inSampleSize) * (height / inSampleSize);
// Anything more than 2x the requested pixels we'll sample down further // Anything more than 2x the requested pixels we'll sample down further
final long totalReqPixelsCap = reqWidth * reqHeight * 2; final long totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels > totalReqPixelsCap) { while (totalPixels > totalReqPixelsCap) {
inSampleSize *= 2; inSampleSize *= 2;
totalPixels /= 2; totalPixels = (width / inSampleSize) * (height / inSampleSize);
} }
} }
return inSampleSize; return inSampleSize;

View File

@@ -52,4 +52,8 @@ public class Utils {
public static boolean hasKitKat() { public static boolean hasKitKat() {
return Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT; return Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT;
} }
public static boolean hasN() {
return Build.VERSION.SDK_INT >= VERSION_CODES.N;
}
} }

View File

@@ -95,7 +95,7 @@ public abstract class Worker {
* @param owner The owner to bind the downloaded image to. * @param owner The owner to bind the downloaded image to.
* @param listener A listener that will be called back once the image has been loaded. * @param listener A listener that will be called back once the image has been loaded.
*/ */
public void loadImage(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean useCache, boolean async, OnImageLoadedListener listener) { public void loadImage(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean keepAspectRatio, boolean useCache, boolean async, OnImageLoadedListener listener) {
if (uri == null) { if (uri == null) {
return; return;
} }
@@ -111,7 +111,7 @@ public abstract class Worker {
if (value == null && !async) { if (value == null && !async) {
// Decode sync. // Decode sync.
value = processBitmap(uri, decodeWidth, decodeHeight, useCache); value = processBitmap(uri, decodeWidth, decodeHeight, keepAspectRatio, useCache);
if (value != null) { if (value != null) {
if (mCache != null && useCache) { if (mCache != null && useCache) {
if (debuggable > 0) { if (debuggable > 0) {
@@ -135,7 +135,7 @@ public abstract class Worker {
listener.onImageLoaded(true); listener.onImageLoaded(true);
} }
} else if (cancelPotentialWork(uri, owner)) { } else if (cancelPotentialWork(uri, owner)) {
final BitmapWorkerTask task = new BitmapWorkerTask(uri, owner, decodeWidth, decodeHeight, useCache, listener); final BitmapWorkerTask task = new BitmapWorkerTask(uri, owner, decodeWidth, decodeHeight, keepAspectRatio, useCache, listener);
final AsyncDrawable asyncDrawable = final AsyncDrawable asyncDrawable =
new AsyncDrawable(mResources, mLoadingBitmap, task); new AsyncDrawable(mResources, mLoadingBitmap, task);
owner.setDrawable(asyncDrawable); owner.setDrawable(asyncDrawable);
@@ -194,7 +194,7 @@ public abstract class Worker {
* {@link Worker#loadImage(String, BitmapOwner, int, int, boolean, boolean, OnImageLoadedListener)} * {@link Worker#loadImage(String, BitmapOwner, int, int, boolean, boolean, OnImageLoadedListener)}
* @return The processed bitmap * @return The processed bitmap
*/ */
protected abstract Bitmap processBitmap(String uri, int decodeWidth, int decodeHeight, boolean useCache); protected abstract Bitmap processBitmap(String uri, int decodeWidth, int decodeHeight, boolean keepAspectRatio, boolean useCache);
/** /**
* @return The {@link Cache} object currently being used by this Worker. * @return The {@link Cache} object currently being used by this Worker.
@@ -263,18 +263,20 @@ public abstract class Worker {
private class BitmapWorkerTask extends AsyncTask<Void, Void, Bitmap> { private class BitmapWorkerTask extends AsyncTask<Void, Void, Bitmap> {
private int mDecodeWidth; private int mDecodeWidth;
private int mDecodeHeight; private int mDecodeHeight;
private boolean mKeepAspectRatio;
private String mUri; private String mUri;
private boolean mCacheImage; private boolean mCacheImage;
private final WeakReference<BitmapOwner> imageViewReference; private final WeakReference<BitmapOwner> imageViewReference;
private final OnImageLoadedListener mOnImageLoadedListener; private final OnImageLoadedListener mOnImageLoadedListener;
public BitmapWorkerTask(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean cacheImage) { public BitmapWorkerTask(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean keepAspectRatio, boolean cacheImage) {
this(uri, owner, decodeWidth, decodeHeight, cacheImage, null); this(uri, owner, decodeWidth, decodeHeight, keepAspectRatio, cacheImage, null);
} }
public BitmapWorkerTask(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean cacheImage, OnImageLoadedListener listener) { public BitmapWorkerTask(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean keepAspectRatio, boolean cacheImage, OnImageLoadedListener listener) {
mDecodeWidth = decodeWidth; mDecodeWidth = decodeWidth;
mDecodeHeight = decodeHeight; mDecodeHeight = decodeHeight;
mKeepAspectRatio = keepAspectRatio;
mCacheImage = cacheImage; mCacheImage = cacheImage;
mUri = uri; mUri = uri;
imageViewReference = new WeakReference<BitmapOwner>(owner); imageViewReference = new WeakReference<BitmapOwner>(owner);
@@ -308,7 +310,7 @@ public abstract class Worker {
// process method (as implemented by a subclass) // process method (as implemented by a subclass)
if (bitmap == null && !isCancelled() && getAttachedOwner() != null if (bitmap == null && !isCancelled() && getAttachedOwner() != null
&& !mExitTasksEarly) { && !mExitTasksEarly) {
bitmap = processBitmap(mUri, mDecodeWidth, mDecodeHeight, mCacheImage); bitmap = processBitmap(mUri, mDecodeWidth, mDecodeHeight, mKeepAspectRatio, mCacheImage);
} }
// If the bitmap was processed and the image cache is available, then add the processed // If the bitmap was processed and the image cache is available, then add the processed

View File

@@ -30,6 +30,7 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
private String mUri; private String mUri;
private int mDecodeWidth; private int mDecodeWidth;
private int mDecodeHeight; private int mDecodeHeight;
private boolean mKeepAspectRatio;
private boolean mUseCache; private boolean mUseCache;
private boolean mAsync; private boolean mAsync;
private Worker.OnImageLoadedListener mListener; private Worker.OnImageLoadedListener mListener;
@@ -162,9 +163,14 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
} }
public void setUri(String uri, int decodeWidth, int decodeHeight, boolean useCache, boolean async) { public void setUri(String uri, int decodeWidth, int decodeHeight, boolean useCache, boolean async) {
this.setUri(uri, decodeWidth, decodeHeight, false, useCache, async);
}
public void setUri(String uri, int decodeWidth, int decodeHeight, boolean keepAspectRatio, boolean useCache, boolean async) {
mUri = uri; mUri = uri;
mDecodeWidth = decodeWidth; mDecodeWidth = decodeWidth;
mDecodeHeight = decodeHeight; mDecodeHeight = decodeHeight;
mKeepAspectRatio = keepAspectRatio;
mUseCache = useCache; mUseCache = useCache;
mAsync = async; mAsync = async;
@@ -188,7 +194,7 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
Fetcher fetcher = Fetcher.getInstance(this.getContext()); Fetcher fetcher = Fetcher.getInstance(this.getContext());
if (mUri != null && fetcher != null) { if (mUri != null && fetcher != null) {
// Get the Bitmap from cache. // Get the Bitmap from cache.
fetcher.loadImage(mUri, this, mDecodeWidth, mDecodeHeight, mUseCache, mAsync, mListener); fetcher.loadImage(mUri, this, mDecodeWidth, mDecodeHeight, mKeepAspectRatio, mUseCache, mAsync, mListener);
} }
} }