mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Fetcher now works using BitmapOwner interface (#95)
ImageView implements BitmapOwner BackgroundDrawable implements BitmapOwner Merge Resizer and Fetcher
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package org.nativescript.widgets;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
@@ -12,7 +13,10 @@ import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.util.Log;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import org.nativescript.widgets.image.BitmapOwner;
|
||||
import org.nativescript.widgets.image.Fetcher;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -20,7 +24,7 @@ import java.util.regex.Pattern;
|
||||
/**
|
||||
* Created by hristov on 6/15/2016.
|
||||
*/
|
||||
public class BorderDrawable extends ColorDrawable {
|
||||
public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
private float density;
|
||||
private String id;
|
||||
|
||||
@@ -42,13 +46,16 @@ public class BorderDrawable extends ColorDrawable {
|
||||
private String clipPath;
|
||||
|
||||
private int backgroundColor;
|
||||
private Bitmap backgroundImage;
|
||||
private String backgroundImage;
|
||||
private Bitmap backgroundBitmap;
|
||||
private String backgroundRepeat;
|
||||
private String backgroundPosition;
|
||||
private CSSValue[] backgroundPositionParsedCSSValues;
|
||||
private String backgroundSize;
|
||||
private CSSValue[] backgroundSizeParsedCSSValues;
|
||||
|
||||
private Drawable drawable;
|
||||
|
||||
public float getDensity() {
|
||||
return density;
|
||||
}
|
||||
@@ -133,10 +140,14 @@ public class BorderDrawable extends ColorDrawable {
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
public Bitmap getBackgroundImage() {
|
||||
public String getBackgroundImage() {
|
||||
return backgroundImage;
|
||||
}
|
||||
|
||||
public Bitmap getBackgroundBitmap() {
|
||||
return backgroundBitmap;
|
||||
}
|
||||
|
||||
public String getBackgroundRepeat() {
|
||||
return backgroundRepeat;
|
||||
}
|
||||
@@ -184,46 +195,6 @@ public class BorderDrawable extends ColorDrawable {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
// For backwards compatibility
|
||||
public void refresh(float borderWidth,
|
||||
int borderColor,
|
||||
float borderRadius,
|
||||
String clipPath,
|
||||
int backgroundColor,
|
||||
Bitmap backgroundImage,
|
||||
String backgroundRepeat,
|
||||
String backgroundPosition,
|
||||
CSSValue[] backgroundPositionParsedCSSValues,
|
||||
String backgroundSize,
|
||||
CSSValue[] backgroundSizeParsedCSSValues) {
|
||||
this.refresh(
|
||||
borderColor,
|
||||
borderColor,
|
||||
borderColor,
|
||||
borderColor,
|
||||
|
||||
borderWidth,
|
||||
borderWidth,
|
||||
borderWidth,
|
||||
borderWidth,
|
||||
|
||||
borderRadius,
|
||||
borderRadius,
|
||||
borderRadius,
|
||||
borderRadius,
|
||||
|
||||
clipPath,
|
||||
|
||||
backgroundColor,
|
||||
backgroundImage,
|
||||
backgroundRepeat,
|
||||
backgroundPosition,
|
||||
backgroundPositionParsedCSSValues,
|
||||
backgroundSize,
|
||||
backgroundSizeParsedCSSValues
|
||||
);
|
||||
}
|
||||
|
||||
public void refresh(int borderTopColor,
|
||||
int borderRightColor,
|
||||
int borderBottomColor,
|
||||
@@ -242,7 +213,9 @@ public class BorderDrawable extends ColorDrawable {
|
||||
String clipPath,
|
||||
|
||||
int backgroundColor,
|
||||
Bitmap backgroundImage,
|
||||
String backgroundImageUri,
|
||||
Bitmap backgroundBitmap,
|
||||
Context context,
|
||||
String backgroundRepeat,
|
||||
String backgroundPosition,
|
||||
CSSValue[] backgroundPositionParsedCSSValues,
|
||||
@@ -267,7 +240,8 @@ public class BorderDrawable extends ColorDrawable {
|
||||
this.clipPath = clipPath;
|
||||
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.backgroundImage = backgroundImage;
|
||||
this.backgroundImage = backgroundImageUri;
|
||||
this.backgroundBitmap = backgroundBitmap;
|
||||
this.backgroundRepeat = backgroundRepeat;
|
||||
this.backgroundPosition = backgroundPosition;
|
||||
this.backgroundPositionParsedCSSValues = backgroundPositionParsedCSSValues;
|
||||
@@ -275,6 +249,12 @@ public class BorderDrawable extends ColorDrawable {
|
||||
this.backgroundSizeParsedCSSValues = backgroundSizeParsedCSSValues;
|
||||
|
||||
this.invalidateSelf();
|
||||
if (backgroundImageUri != null) {
|
||||
Fetcher fetcher = Fetcher.getInstance(context);
|
||||
// TODO: Implement option to pass load-mode like in ImageView class.
|
||||
boolean loadAsync = backgroundImageUri.startsWith("http");
|
||||
fetcher.loadImage(backgroundImageUri, this, 0, 0, true, loadAsync, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -312,20 +292,20 @@ public class BorderDrawable extends ColorDrawable {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.backgroundImage != null) {
|
||||
if (this.backgroundBitmap != null) {
|
||||
BackgroundDrawParams params = this.getDrawParams(bounds.width(), bounds.height());
|
||||
Matrix transform = new Matrix();
|
||||
if (params.sizeX > 0 && params.sizeY > 0) {
|
||||
float scaleX = params.sizeX / this.backgroundImage.getWidth();
|
||||
float scaleY = params.sizeY / this.backgroundImage.getHeight();
|
||||
float scaleX = params.sizeX / this.backgroundBitmap.getWidth();
|
||||
float scaleY = params.sizeY / this.backgroundBitmap.getHeight();
|
||||
transform.setScale(scaleX, scaleY, 0, 0);
|
||||
} else {
|
||||
params.sizeX = this.backgroundImage.getWidth();
|
||||
params.sizeY = this.backgroundImage.getHeight();
|
||||
params.sizeX = this.backgroundBitmap.getWidth();
|
||||
params.sizeY = this.backgroundBitmap.getHeight();
|
||||
}
|
||||
transform.postTranslate(params.posX - leftBackoffAntialias, params.posY - topBackoffAntialias);
|
||||
|
||||
BitmapShader shader = new BitmapShader(this.backgroundImage, android.graphics.Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT);
|
||||
BitmapShader shader = new BitmapShader(this.backgroundBitmap, android.graphics.Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT);
|
||||
shader.setLocalMatrix(transform);
|
||||
|
||||
Paint backgroundImagePaint = new Paint();
|
||||
@@ -611,8 +591,8 @@ public class BorderDrawable extends ColorDrawable {
|
||||
}
|
||||
}
|
||||
|
||||
float imageWidth = this.backgroundImage.getWidth();
|
||||
float imageHeight = this.backgroundImage.getHeight();
|
||||
float imageWidth = this.backgroundBitmap.getWidth();
|
||||
float imageHeight = this.backgroundBitmap.getHeight();
|
||||
|
||||
// size
|
||||
if (this.backgroundSize != null && !this.backgroundSize.isEmpty()) {
|
||||
@@ -744,12 +724,30 @@ public class BorderDrawable extends ColorDrawable {
|
||||
"clipPath: " + this.clipPath + "; " +
|
||||
"backgroundColor: " + this.backgroundColor + "; " +
|
||||
"backgroundImage: " + this.backgroundImage + "; " +
|
||||
"backgroundBitmap: " + this.backgroundBitmap + "; " +
|
||||
"backgroundRepeat: " + this.backgroundRepeat + "; " +
|
||||
"backgroundPosition: " + this.backgroundPosition + "; " +
|
||||
"backgroundSize: " + this.backgroundSize + "; "
|
||||
;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBitmap(Bitmap value) {
|
||||
backgroundBitmap = value;
|
||||
invalidateSelf();
|
||||
drawable = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawable(Drawable asyncDrawable) {
|
||||
drawable = asyncDrawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawable() {
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private class BackgroundDrawParams {
|
||||
private boolean repeatX = true;
|
||||
private boolean repeatY = true;
|
||||
|
||||
@@ -66,11 +66,8 @@ public class Cache {
|
||||
* called directly by other classes, instead use
|
||||
* {@link Cache#getInstance(CacheParams)} to fetch an Cache
|
||||
* instance.
|
||||
*
|
||||
* @param cacheParams The cache parameters to use to initialize the cache
|
||||
*/
|
||||
private Cache(CacheParams cacheParams) {
|
||||
init(cacheParams);
|
||||
private Cache() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,9 +77,10 @@ public class Cache {
|
||||
*/
|
||||
public static Cache getInstance(CacheParams cacheParams) {
|
||||
if (instance == null) {
|
||||
instance = new Cache(cacheParams);
|
||||
instance = new Cache();
|
||||
}
|
||||
else if (instance.mParams != cacheParams) {
|
||||
|
||||
if (instance.mParams != cacheParams) {
|
||||
instance.init(cacheParams);
|
||||
}
|
||||
|
||||
@@ -95,6 +93,12 @@ public class Cache {
|
||||
* @param cacheParams The cache parameters to initialize the cache
|
||||
*/
|
||||
private void init(CacheParams cacheParams) {
|
||||
clearCache();
|
||||
if (mReusableBitmaps != null) {
|
||||
mReusableBitmaps.clear();
|
||||
mReusableBitmaps = null;
|
||||
}
|
||||
|
||||
mParams = cacheParams;
|
||||
|
||||
// Set up memory cache
|
||||
@@ -142,12 +146,6 @@ public class Cache {
|
||||
return bitmapSize == 0 ? 1 : bitmapSize;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
clearCache();
|
||||
if (mReusableBitmaps != null) {
|
||||
mReusableBitmaps.clear();
|
||||
mReusableBitmaps = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.nativescript.widgets.image;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -36,9 +39,9 @@ import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* A simple subclass of {@link Resizer} that fetch and resize images from a file, resource or URL.
|
||||
* A simple subclass of {@link Worker} that fetch and resize images from a file, resource or URL.
|
||||
*/
|
||||
public class Fetcher extends Resizer {
|
||||
public class Fetcher extends Worker {
|
||||
private static final int HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
private static final String HTTP_CACHE_DIR = "http";
|
||||
private static final int IO_BUFFER_SIZE = 8 * 1024;
|
||||
@@ -154,10 +157,6 @@ public class Fetcher extends Resizer {
|
||||
* @return The downloaded and resized bitmap
|
||||
*/
|
||||
private Bitmap processHttp(String data, int decodeWidth, int decodeHeight) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processHttp - " + data);
|
||||
}
|
||||
|
||||
final String key = Cache.hashKeyForDisk(data);
|
||||
FileDescriptor fileDescriptor = null;
|
||||
FileInputStream fileInputStream = null;
|
||||
@@ -224,17 +223,13 @@ public class Fetcher extends Resizer {
|
||||
}
|
||||
|
||||
private Bitmap processHttpNoCache(String data, int decodeWidth, int decodeHeight) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processHttp - " + data);
|
||||
}
|
||||
|
||||
ByteArrayOutputStreamInternal outputStream = null;
|
||||
Bitmap bitmap = null;
|
||||
|
||||
try {
|
||||
outputStream = new ByteArrayOutputStreamInternal();
|
||||
if (downloadUrlToStream(data, outputStream)) {
|
||||
bitmap = decodeSampledBitmapFromByteArray(outputStream.getBuffer(), decodeWidth, decodeHeight, getCache());
|
||||
bitmap = decodeSampledBitmapFromByteArray(outputStream.getBuffer(), decodeWidth, decodeHeight, getCache());
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "processHttpNoCache - " + e);
|
||||
@@ -251,38 +246,30 @@ public class Fetcher extends Resizer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight, boolean useCache) {
|
||||
if (data instanceof String) {
|
||||
String stringData = String.valueOf(data);
|
||||
if (stringData.startsWith(FILE_PREFIX)) {
|
||||
String filename = stringData.substring(FILE_PREFIX.length());
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processFile - " + filename);
|
||||
}
|
||||
return decodeSampledBitmapFromFile(filename, decodeWidth, decodeHeight, getCache());
|
||||
} else if (stringData.startsWith(RESOURCE_PREFIX)) {
|
||||
String resPath = stringData.substring(RESOURCE_PREFIX.length());
|
||||
int resId = mResources.getIdentifier(resPath, "drawable", mPackageName);
|
||||
if (resId > 0) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processResource - " + resId);
|
||||
}
|
||||
return decodeSampledBitmapFromResource(mResources, resId, decodeWidth, decodeHeight, getCache());
|
||||
} else {
|
||||
Log.v(TAG, "Missing Image with resourceID: " + stringData);
|
||||
}
|
||||
} else {
|
||||
if (useCache && mHttpDiskCache != null) {
|
||||
return processHttp(stringData, decodeWidth, decodeHeight);
|
||||
} else {
|
||||
return processHttpNoCache(stringData, decodeWidth, decodeHeight);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.v(TAG, "Invalid Value: " + String.valueOf(data));
|
||||
protected Bitmap processBitmap(String uri, int decodeWidth, int decodeHeight, boolean useCache) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "process: " + uri);
|
||||
}
|
||||
|
||||
return null;
|
||||
if (uri.startsWith(FILE_PREFIX)) {
|
||||
String filename = uri.substring(FILE_PREFIX.length());
|
||||
return decodeSampledBitmapFromFile(filename, decodeWidth, decodeHeight, getCache());
|
||||
} else if (uri.startsWith(RESOURCE_PREFIX)) {
|
||||
String resPath = uri.substring(RESOURCE_PREFIX.length());
|
||||
int resId = mResources.getIdentifier(resPath, "drawable", mPackageName);
|
||||
if (resId > 0) {
|
||||
return decodeSampledBitmapFromResource(mResources, resId, decodeWidth, decodeHeight, getCache());
|
||||
} else {
|
||||
Log.v(TAG, "Missing Image with resourceID: " + uri);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (useCache && mHttpDiskCache != null) {
|
||||
return processHttp(uri, decodeWidth, decodeHeight);
|
||||
} else {
|
||||
return processHttpNoCache(uri, decodeWidth, decodeHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,4 +330,229 @@ public class Fetcher extends Resizer {
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and sample down a bitmap from resources to the requested width and height.
|
||||
*
|
||||
* @param res The resources object containing the image data
|
||||
* @param resId The resource id of the image data
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
||||
* @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
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
|
||||
int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// BEGIN_INCLUDE (read_bitmap_dimensions)
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeResource(res, resId, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
if (reqWidth > 0 && reqHeight > 0) {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
}
|
||||
else {
|
||||
options.inSampleSize = 1;
|
||||
}
|
||||
|
||||
// END_INCLUDE (read_bitmap_dimensions)
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
return BitmapFactory.decodeResource(res, resId, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and sample down a bitmap from a file to the requested width and height.
|
||||
*
|
||||
* @param filename The full path of the file to decode
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
||||
* @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
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromFile(String filename,
|
||||
int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(filename, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
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 (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
return BitmapFactory.decodeFile(filename, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and sample down a bitmap from a file input stream to the requested width and height.
|
||||
*
|
||||
* @param fileDescriptor The file descriptor to read from
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
||||
* @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
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromDescriptor(
|
||||
FileDescriptor fileDescriptor, int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
if (reqWidth > 0 && reqHeight > 0) {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
}
|
||||
else {
|
||||
options.inSampleSize = 1;
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
Bitmap results = null;
|
||||
try {
|
||||
// This can throw an error on a corrupted image when using an inBitmap
|
||||
results = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// clear the inBitmap and try again
|
||||
options.inBitmap = null;
|
||||
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...
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampledBitmapFromByteArray(
|
||||
byte[] buffer, int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
if (reqWidth > 0 && reqHeight > 0) {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
}
|
||||
else {
|
||||
options.inSampleSize = 1;
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
return BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
|
||||
* bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
|
||||
* 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.
|
||||
*
|
||||
* @param options An options object with out* params already populated (run through a decode*
|
||||
* method with inJustDecodeBounds==true
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @return The value to be used for inSampleSize
|
||||
*/
|
||||
public static int calculateInSampleSize(BitmapFactory.Options options,
|
||||
int reqWidth, int reqHeight) {
|
||||
// BEGIN_INCLUDE (calculate_sample_size)
|
||||
// Raw height and width of image
|
||||
final int height = options.outHeight;
|
||||
final int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
|
||||
final int halfHeight = height / 2;
|
||||
final int halfWidth = width / 2;
|
||||
|
||||
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
|
||||
// height and width larger than the requested height and width.
|
||||
while ((halfHeight / inSampleSize) > reqHeight
|
||||
&& (halfWidth / inSampleSize) > reqWidth) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
|
||||
// This offers some additional logic in case the image has a strange
|
||||
// aspect ratio. For example, a panorama may have a much larger
|
||||
// width than height. In these cases the total pixels might still
|
||||
// end up being too large to fit comfortably in memory, so we should
|
||||
// be more aggressive with sample down the image (=larger inSampleSize).
|
||||
|
||||
long totalPixels = width * height / inSampleSize;
|
||||
|
||||
// Anything more than 2x the requested pixels we'll sample down further
|
||||
final long totalReqPixelsCap = reqWidth * reqHeight * 2;
|
||||
|
||||
while (totalPixels > totalReqPixelsCap) {
|
||||
inSampleSize *= 2;
|
||||
totalPixels /= 2;
|
||||
}
|
||||
}
|
||||
return inSampleSize;
|
||||
// END_INCLUDE (calculate_sample_size)
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
private static void addInBitmapOptions(BitmapFactory.Options options, Cache cache) {
|
||||
//BEGIN_INCLUDE(add_bitmap_options)
|
||||
// inBitmap only works with mutable bitmaps so force the decoder to
|
||||
// return mutable bitmaps.
|
||||
options.inMutable = true;
|
||||
|
||||
if (cache != null) {
|
||||
// Try and find a bitmap to use for inBitmap
|
||||
Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
|
||||
|
||||
if (inBitmap != null) {
|
||||
options.inBitmap = inBitmap;
|
||||
}
|
||||
}
|
||||
//END_INCLUDE(add_bitmap_options)
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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 org.nativescript.widgets.image;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A simple subclass of {@link Worker} that resize images given a target width
|
||||
* and height. Useful for when the input images might be too large to simply load directly into
|
||||
* memory.
|
||||
*/
|
||||
public abstract class Resizer extends Worker {
|
||||
protected Resizer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
/**
|
||||
* Decode and sample down a bitmap from resources to the requested width and height.
|
||||
*
|
||||
* @param res The resources object containing the image data
|
||||
* @param resId The resource id of the image data
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
||||
* @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
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
|
||||
int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// BEGIN_INCLUDE (read_bitmap_dimensions)
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeResource(res, resId, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
if (reqWidth > 0 && reqHeight > 0) {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
}
|
||||
else {
|
||||
options.inSampleSize = 1;
|
||||
}
|
||||
|
||||
// END_INCLUDE (read_bitmap_dimensions)
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
return BitmapFactory.decodeResource(res, resId, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and sample down a bitmap from a file to the requested width and height.
|
||||
*
|
||||
* @param filename The full path of the file to decode
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
||||
* @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
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromFile(String filename,
|
||||
int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(filename, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
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 (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
return BitmapFactory.decodeFile(filename, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and sample down a bitmap from a file input stream to the requested width and height.
|
||||
*
|
||||
* @param fileDescriptor The file descriptor to read from
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
||||
* @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
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromDescriptor(
|
||||
FileDescriptor fileDescriptor, int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
if (reqWidth > 0 && reqHeight > 0) {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
}
|
||||
else {
|
||||
options.inSampleSize = 1;
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
Bitmap results = null;
|
||||
try {
|
||||
// This can throw an error on a corrupted image when using an inBitmap
|
||||
results = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// clear the inBitmap and try again
|
||||
options.inBitmap = null;
|
||||
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...
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampledBitmapFromByteArray(
|
||||
byte[] buffer, int reqWidth, int reqHeight, Cache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
|
||||
|
||||
// If requested width/height were not specified - decode in full size.
|
||||
if (reqWidth > 0 && reqHeight > 0) {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
}
|
||||
else {
|
||||
options.inSampleSize = 1;
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
return BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
|
||||
* bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
|
||||
* 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.
|
||||
*
|
||||
* @param options An options object with out* params already populated (run through a decode*
|
||||
* method with inJustDecodeBounds==true
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @return The value to be used for inSampleSize
|
||||
*/
|
||||
public static int calculateInSampleSize(BitmapFactory.Options options,
|
||||
int reqWidth, int reqHeight) {
|
||||
// BEGIN_INCLUDE (calculate_sample_size)
|
||||
// Raw height and width of image
|
||||
final int height = options.outHeight;
|
||||
final int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
|
||||
final int halfHeight = height / 2;
|
||||
final int halfWidth = width / 2;
|
||||
|
||||
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
|
||||
// height and width larger than the requested height and width.
|
||||
while ((halfHeight / inSampleSize) > reqHeight
|
||||
&& (halfWidth / inSampleSize) > reqWidth) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
|
||||
// This offers some additional logic in case the image has a strange
|
||||
// aspect ratio. For example, a panorama may have a much larger
|
||||
// width than height. In these cases the total pixels might still
|
||||
// end up being too large to fit comfortably in memory, so we should
|
||||
// be more aggressive with sample down the image (=larger inSampleSize).
|
||||
|
||||
long totalPixels = width * height / inSampleSize;
|
||||
|
||||
// Anything more than 2x the requested pixels we'll sample down further
|
||||
final long totalReqPixelsCap = reqWidth * reqHeight * 2;
|
||||
|
||||
while (totalPixels > totalReqPixelsCap) {
|
||||
inSampleSize *= 2;
|
||||
totalPixels /= 2;
|
||||
}
|
||||
}
|
||||
return inSampleSize;
|
||||
// END_INCLUDE (calculate_sample_size)
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
private static void addInBitmapOptions(BitmapFactory.Options options, Cache cache) {
|
||||
//BEGIN_INCLUDE(add_bitmap_options)
|
||||
// inBitmap only works with mutable bitmaps so force the decoder to
|
||||
// return mutable bitmaps.
|
||||
options.inMutable = true;
|
||||
|
||||
if (cache != null) {
|
||||
// Try and find a bitmap to use for inBitmap
|
||||
Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
|
||||
|
||||
if (inBitmap != null) {
|
||||
options.inBitmap = inBitmap;
|
||||
}
|
||||
}
|
||||
//END_INCLUDE(add_bitmap_options)
|
||||
}
|
||||
}
|
||||
@@ -27,26 +27,6 @@ import android.os.StrictMode;
|
||||
public class Utils {
|
||||
private Utils() {};
|
||||
|
||||
@TargetApi(VERSION_CODES.HONEYCOMB)
|
||||
public static void enableStrictMode() {
|
||||
if (Utils.hasGingerbread()) {
|
||||
StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
|
||||
new StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog();
|
||||
StrictMode.VmPolicy.Builder vmPolicyBuilder =
|
||||
new StrictMode.VmPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog();
|
||||
|
||||
if (Utils.hasHoneycomb()) {
|
||||
threadPolicyBuilder.penaltyFlashScreen();
|
||||
}
|
||||
StrictMode.setThreadPolicy(threadPolicyBuilder.build());
|
||||
StrictMode.setVmPolicy(vmPolicyBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasFroyo() {
|
||||
// Can use static final constants like FROYO, declared in later versions
|
||||
// of the OS since they are inlined at compile time. This is guaranteed behavior.
|
||||
|
||||
@@ -79,61 +79,60 @@ public abstract class Worker {
|
||||
|
||||
/**
|
||||
* Load an image specified by the data parameter into an ImageView (override
|
||||
* {@link Worker#processBitmap(Object, int, int, boolean)} to define the processing logic). A memory and
|
||||
* {@link Worker#processBitmap(String, int, int, boolean)} to define the processing logic). A memory and
|
||||
* disk cache will be used if an {@link Cache} has been added using
|
||||
* {@link Worker#addImageCache(Cache)}. If the
|
||||
* image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
|
||||
* will be created to asynchronously load the bitmap.
|
||||
*
|
||||
* @param data The URL of the image to download.
|
||||
* @param imageView The ImageView to bind the downloaded image to.
|
||||
* @param uri The URI of the image to download.
|
||||
* @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.
|
||||
*/
|
||||
public void loadImage(Object data, ImageView imageView, int decodeWidth, int decodeHeight, boolean useCache, boolean async, OnImageLoadedListener listener) {
|
||||
if (data == null) {
|
||||
public void loadImage(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean useCache, boolean async, OnImageLoadedListener listener) {
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap value = null;
|
||||
String dataString = String.valueOf(data);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "loadImage on: " + imageView + " to: " + dataString);
|
||||
Log.v(TAG, "loadImage on: " + owner + " to: " + uri);
|
||||
}
|
||||
|
||||
if (mCache != null && useCache) {
|
||||
value = mCache.getBitmapFromMemCache(dataString);
|
||||
value = mCache.getBitmapFromMemCache(uri);
|
||||
}
|
||||
|
||||
if (value == null && !async) {
|
||||
// Decode sync.
|
||||
value = processBitmap(data, decodeWidth, decodeHeight, useCache);
|
||||
value = processBitmap(uri, decodeWidth, decodeHeight, useCache);
|
||||
if (value != null) {
|
||||
if (mCache != null && useCache) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "loadImage.addBitmapToCache: " + imageView + ", src: " + dataString);
|
||||
Log.v(TAG, "loadImage.addBitmapToCache: " + owner + ", src: " + uri);
|
||||
}
|
||||
mCache.addBitmapToCache(dataString, value);
|
||||
mCache.addBitmapToCache(uri, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
// Bitmap found in memory cache
|
||||
// Bitmap found in memory cache or loaded sync.
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "Set ImageBitmap on: " + imageView + " to: " + dataString);
|
||||
Log.v(TAG, "Set ImageBitmap on: " + owner + " to: " + uri);
|
||||
}
|
||||
imageView.setImageBitmap(value);
|
||||
owner.setBitmap(value);
|
||||
if (listener != null) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "OnImageLoadedListener on: " + imageView + " to: " + dataString);
|
||||
Log.v(TAG, "OnImageLoadedListener on: " + owner + " to: " + uri);
|
||||
}
|
||||
listener.onImageLoaded(true);
|
||||
}
|
||||
} else if (cancelPotentialWork(data, imageView)) {
|
||||
final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView, decodeWidth, decodeHeight, useCache, listener);
|
||||
} else if (cancelPotentialWork(uri, owner)) {
|
||||
final BitmapWorkerTask task = new BitmapWorkerTask(uri, owner, decodeWidth, decodeHeight, useCache, listener);
|
||||
final AsyncDrawable asyncDrawable =
|
||||
new AsyncDrawable(mResources, mLoadingBitmap, task);
|
||||
imageView.setImageDrawable(asyncDrawable);
|
||||
owner.setDrawable(asyncDrawable);
|
||||
|
||||
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
|
||||
// framework and slightly modified. Refer to the docs at the top of the class
|
||||
@@ -185,11 +184,11 @@ public abstract class Worker {
|
||||
* the final bitmap. This will be executed in a background thread and be long running. For
|
||||
* example, you could resize a large bitmap here, or pull down an image from the network.
|
||||
*
|
||||
* @param data The data to identify which image to process, as provided by
|
||||
* {@link Worker#loadImage(Object, ImageView, int, int, boolean, boolean, OnImageLoadedListener)}
|
||||
* @param uri The URI to identify which image to process, as provided by
|
||||
* {@link Worker#loadImage(String, BitmapOwner, int, int, boolean, boolean, OnImageLoadedListener)}
|
||||
* @return The processed bitmap
|
||||
*/
|
||||
protected abstract Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight, boolean useCache);
|
||||
protected abstract Bitmap processBitmap(String uri, int decodeWidth, int decodeHeight, boolean useCache);
|
||||
|
||||
/**
|
||||
* @return The {@link Cache} object currently being used by this Worker.
|
||||
@@ -200,15 +199,14 @@ public abstract class Worker {
|
||||
|
||||
/**
|
||||
* Cancels any pending work attached to the provided ImageView.
|
||||
* @param imageView
|
||||
* @param owner
|
||||
*/
|
||||
public static void cancelWork(ImageView imageView) {
|
||||
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
|
||||
public static void cancelWork(BitmapOwner owner) {
|
||||
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(owner);
|
||||
if (bitmapWorkerTask != null) {
|
||||
bitmapWorkerTask.cancel(true);
|
||||
if (debuggable > 0) {
|
||||
final Object bitmapData = bitmapWorkerTask.mData;
|
||||
Log.v(TAG, "cancelWork - cancelled work for " + bitmapData);
|
||||
Log.v(TAG, "cancelWork - cancelled work for " + bitmapWorkerTask.mUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,15 +217,15 @@ public abstract class Worker {
|
||||
* Returns false if the work in progress deals with the same data. The work is not
|
||||
* stopped in that case.
|
||||
*/
|
||||
public static boolean cancelPotentialWork(Object data, ImageView imageView) {
|
||||
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
|
||||
public static boolean cancelPotentialWork(String uri, BitmapOwner owner) {
|
||||
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(owner);
|
||||
|
||||
if (bitmapWorkerTask != null) {
|
||||
final Object bitmapData = bitmapWorkerTask.mData;
|
||||
if (bitmapData == null || !bitmapData.equals(data)) {
|
||||
final String mUri = bitmapWorkerTask.mUri;
|
||||
if (mUri == null || !mUri.equals(uri)) {
|
||||
bitmapWorkerTask.cancel(true);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "cancelPotentialWork - cancelled work for " + data);
|
||||
Log.v(TAG, "cancelPotentialWork - cancelled work for " + uri);
|
||||
}
|
||||
} else {
|
||||
// The same work is already in progress.
|
||||
@@ -238,13 +236,13 @@ public abstract class Worker {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param imageView Any imageView
|
||||
* @param owner The owner that requested the bitmap;
|
||||
* @return Retrieve the currently active work task (if any) associated with this imageView.
|
||||
* null if there is no such task.
|
||||
*/
|
||||
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
|
||||
if (imageView != null) {
|
||||
final Drawable drawable = imageView.getDrawable();
|
||||
private static BitmapWorkerTask getBitmapWorkerTask(BitmapOwner owner) {
|
||||
if (owner != null) {
|
||||
final Drawable drawable = owner.getDrawable();
|
||||
if (drawable instanceof AsyncDrawable) {
|
||||
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
|
||||
return asyncDrawable.getBitmapWorkerTask();
|
||||
@@ -259,21 +257,21 @@ public abstract class Worker {
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, Bitmap> {
|
||||
private int mDecodeWidth;
|
||||
private int mDecodeHeight;
|
||||
private Object mData;
|
||||
private String mUri;
|
||||
private boolean mCacheImage;
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
private final WeakReference<BitmapOwner> imageViewReference;
|
||||
private final OnImageLoadedListener mOnImageLoadedListener;
|
||||
|
||||
public BitmapWorkerTask(Object data, ImageView imageView, int decodeWidth, int decodeHeight, boolean cacheImage) {
|
||||
this(data, imageView, decodeWidth, decodeHeight, cacheImage, null);
|
||||
public BitmapWorkerTask(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean cacheImage) {
|
||||
this(uri, owner, decodeWidth, decodeHeight, cacheImage, null);
|
||||
}
|
||||
|
||||
public BitmapWorkerTask(Object data, ImageView imageView, int decodeWidth, int decodeHeight, boolean cacheImage, OnImageLoadedListener listener) {
|
||||
public BitmapWorkerTask(String uri, BitmapOwner owner, int decodeWidth, int decodeHeight, boolean cacheImage, OnImageLoadedListener listener) {
|
||||
mDecodeWidth = decodeWidth;
|
||||
mDecodeHeight = decodeHeight;
|
||||
mCacheImage = cacheImage;
|
||||
mData = data;
|
||||
imageViewReference = new WeakReference<ImageView>(imageView);
|
||||
mUri = uri;
|
||||
imageViewReference = new WeakReference<BitmapOwner>(owner);
|
||||
mOnImageLoadedListener = listener;
|
||||
}
|
||||
|
||||
@@ -282,7 +280,7 @@ public abstract class Worker {
|
||||
*/
|
||||
@Override
|
||||
protected Bitmap doInBackground(Void... params) {
|
||||
final String dataString = String.valueOf(mData);
|
||||
final String dataString = String.valueOf(mUri);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "doInBackground - starting work: " + imageViewReference.get() + ", on: " + dataString);
|
||||
}
|
||||
@@ -303,9 +301,9 @@ public abstract class Worker {
|
||||
// another thread and the ImageView that was originally bound to this task is still
|
||||
// bound back to this task and our "exit early" flag is not set, then call the main
|
||||
// process method (as implemented by a subclass)
|
||||
if (bitmap == null && !isCancelled() && getAttachedImageView() != null
|
||||
if (bitmap == null && !isCancelled() && getAttachedOwner() != null
|
||||
&& !mExitTasksEarly) {
|
||||
bitmap = processBitmap(mData, mDecodeWidth, mDecodeHeight, mCacheImage);
|
||||
bitmap = processBitmap(mUri, mDecodeWidth, mDecodeHeight, mCacheImage);
|
||||
}
|
||||
|
||||
// If the bitmap was processed and the image cache is available, then add the processed
|
||||
@@ -339,27 +337,26 @@ public abstract class Worker {
|
||||
value = null;
|
||||
}
|
||||
|
||||
final String dataString = String.valueOf(mData);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "onPostExecute - setting bitmap for: " + imageViewReference.get() + " src: " + dataString);
|
||||
Log.v(TAG, "onPostExecute - setting bitmap for: " + imageViewReference.get() + " src: " + mUri);
|
||||
}
|
||||
|
||||
final ImageView imageView = getAttachedImageView();
|
||||
final BitmapOwner owner = getAttachedOwner();
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "onPostExecute - current ImageView: " + imageView);
|
||||
Log.v(TAG, "onPostExecute - current ImageView: " + owner);
|
||||
}
|
||||
|
||||
if (value != null && imageView != null) {
|
||||
if (value != null && owner != null) {
|
||||
success = true;
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "Set ImageDrawable on: " + imageView + " to: " + dataString);
|
||||
Log.v(TAG, "Set ImageDrawable on: " + owner + " to: " + mUri);
|
||||
}
|
||||
imageView.setImageBitmap(value);
|
||||
owner.setBitmap(value);
|
||||
}
|
||||
|
||||
if (mOnImageLoadedListener != null) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "OnImageLoadedListener on: " + imageView + " to: " + dataString);
|
||||
Log.v(TAG, "OnImageLoadedListener on: " + owner + " to: " + mUri);
|
||||
}
|
||||
mOnImageLoadedListener.onImageLoaded(success);
|
||||
}
|
||||
@@ -377,12 +374,12 @@ public abstract class Worker {
|
||||
* Returns the ImageView associated with this task as long as the ImageView's task still
|
||||
* points to this task as well. Returns null otherwise.
|
||||
*/
|
||||
private ImageView getAttachedImageView() {
|
||||
final ImageView imageView = imageViewReference.get();
|
||||
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
|
||||
private BitmapOwner getAttachedOwner() {
|
||||
final BitmapOwner owner = imageViewReference.get();
|
||||
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(owner);
|
||||
|
||||
if (this == bitmapWorkerTask) {
|
||||
return imageView;
|
||||
return owner;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -7,13 +7,14 @@ import android.content.Context;
|
||||
import android.graphics.*;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import org.nativescript.widgets.image.BitmapOwner;
|
||||
import org.nativescript.widgets.image.Fetcher;
|
||||
import org.nativescript.widgets.image.Worker;
|
||||
|
||||
/**
|
||||
* @author hhristov
|
||||
*/
|
||||
public class ImageView extends android.widget.ImageView {
|
||||
public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
private static final double EPSILON = 1E-05;
|
||||
|
||||
private Path path = new Path();
|
||||
@@ -266,4 +267,14 @@ public class ImageView extends android.widget.ImageView {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBitmap(Bitmap value) {
|
||||
this.setImageBitmap(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawable(Drawable asyncDrawable) {
|
||||
this.setImageDrawable(asyncDrawable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.nativescript.widgets.image;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* Created by hhristov on 4/18/17.
|
||||
*/
|
||||
|
||||
public interface BitmapOwner {
|
||||
void setBitmap(Bitmap value);
|
||||
void setDrawable(Drawable asyncDrawable);
|
||||
Drawable getDrawable();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user