Image background is drawn first not after bitmap. (#57)

Image file cache is used only for images downloaded through web.
Image cache is now caching Bitmap instead of BitmapDrawables (drawables are not immutable).
This commit is contained in:
Hristo Hristov
2016-10-13 16:39:15 +03:00
committed by GitHub
parent ace4777811
commit 626e148e2b
4 changed files with 162 additions and 352 deletions

View File

@@ -19,10 +19,8 @@ package org.nativescript.widgets.image;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build.VERSION_CODES;
import android.os.Environment;
import android.os.StatFs;
@@ -30,11 +28,6 @@ import android.support.v4.util.LruCache;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -56,32 +49,16 @@ public class Cache {
// Default memory cache size in kilobytes
private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 5MB
// Default disk cache size in bytes
private static final int DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
// Compression settings when writing images to disk cache
private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG;
private static final int DEFAULT_COMPRESS_QUALITY = 70;
private static final int DISK_CACHE_INDEX = 0;
// Constants to easily toggle various caches
private static final boolean DEFAULT_MEM_CACHE_ENABLED = true;
private static final boolean DEFAULT_DISK_CACHE_ENABLED = true;
private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false;
private static final String IMAGE_CACHE_DIR = "images";
private static Cache instance;
private DiskLruCache mDiskLruCache;
private LruCache<String, BitmapDrawable> mMemoryCache;
private LruCache<String, Bitmap> mMemoryCache;
private CacheParams mParams;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private Set<SoftReference<Bitmap>> mReusableBitmaps;
private Fetcher mFetcher;
/**
* Create a new Cache object using the specified parameters. This should not be
* called directly by other classes, instead use
@@ -102,7 +79,6 @@ public class Cache {
public static Cache getInstance(CacheParams cacheParams) {
if (instance == null) {
instance = new Cache(cacheParams);
instance.init(cacheParams);
}
else if (instance.mParams != cacheParams) {
instance.init(cacheParams);
@@ -119,7 +95,6 @@ public class Cache {
private void init(CacheParams cacheParams) {
mParams = cacheParams;
//BEGIN_INCLUDE(init_memory_cache)
// Set up memory cache
if (mParams.memoryCacheEnabled) {
if (Worker.debuggable > 0) {
@@ -140,18 +115,18 @@ public class Cache {
Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
}
mMemoryCache = new LruCache<String, BitmapDrawable>(mParams.memCacheSize) {
mMemoryCache = new LruCache<String, Bitmap>(mParams.memCacheSize) {
/**
* Notify the removed entry that is no longer being cached
*/
@Override
protected void entryRemoved(boolean evicted, String key,
BitmapDrawable oldValue, BitmapDrawable newValue) {
Bitmap oldValue, Bitmap newValue) {
if (Utils.hasHoneycomb()) {
// We're running on Honeycomb or later, so add the bitmap
// to a SoftReference set for possible use with inBitmap later
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue));
}
}
@@ -160,54 +135,12 @@ public class Cache {
* for a bitmap cache
*/
@Override
protected int sizeOf(String key, BitmapDrawable value) {
protected int sizeOf(String key, Bitmap value) {
final int bitmapSize = getBitmapSize(value) / 1024;
return bitmapSize == 0 ? 1 : bitmapSize;
}
};
}
//END_INCLUDE(init_memory_cache)
// // By default the disk cache is not initialized here as it should be initialized
// // on a separate thread due to disk access.
// if (cacheParams.initDiskCacheOnCreate) {
// // Set up disk cache
// initDiskCache();
// }
}
/**
* Initializes the disk cache. Note that this includes disk access so this should not be
* executed on the main/UI thread. By default an Cache does not initialize the disk
* cache when it is created, instead you should call initDiskCache() to initialize it on a
* background thread.
*/
public void initDiskCache() {
// Set up disk cache
synchronized (mDiskCacheLock) {
if (mDiskLruCache == null || mDiskLruCache.isClosed()) {
File diskCacheDir = mParams.diskCacheDir;
if (mParams.diskCacheEnabled && diskCacheDir != null) {
if (!diskCacheDir.exists()) {
diskCacheDir.mkdirs();
}
if (getUsableSpace(diskCacheDir) > mParams.diskCacheSize) {
try {
mDiskLruCache = DiskLruCache.open(
diskCacheDir, 1, 1, mParams.diskCacheSize);
if (Worker.debuggable > 0) {
Log.v(TAG, "Disk cache initialized");
}
} catch (final IOException e) {
mParams.diskCacheDir = null;
Log.e(TAG, "initDiskCache - " + e);
}
}
}
}
mDiskCacheStarting = false;
mDiskCacheLock.notifyAll();
}
}
/**
@@ -216,66 +149,31 @@ public class Cache {
* @param data Unique identifier for the bitmap to store
* @param value The bitmap drawable to store
*/
public void addBitmapToCache(String data, BitmapDrawable value, boolean useDiskCache) {
//BEGIN_INCLUDE(add_bitmap_to_cache)
public void addBitmapToCache(String data, Bitmap value) {
if (data == null || value == null) {
return;
}
// Add to memory cache
if (mMemoryCache != null) {
mMemoryCache.put(data, value);
}
if (!useDiskCache) {
return;
}
synchronized (mDiskCacheLock) {
// Add to disk cache
if (mDiskLruCache != null) {
final String key = hashKeyForDisk(data);
OutputStream out = null;
try {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot == null) {
final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
out = editor.newOutputStream(DISK_CACHE_INDEX);
value.getBitmap().compress(
mParams.compressFormat, mParams.compressQuality, out);
editor.commit();
out.close();
}
} else {
snapshot.getInputStream(DISK_CACHE_INDEX).close();
}
} catch (final IOException e) {
Log.e(TAG, "addBitmapToCache - " + e);
} catch (Exception e) {
Log.e(TAG, "addBitmapToCache - " + e);
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
}
}
Bitmap currentValue = mMemoryCache.get(data);
// NOTE: If we have existing we probably loaded it sync so we don't want to add the new one,
// because this will make the previous bitmap free for reuse but it is used somewhere.
// Probably won't happen often.
if (currentValue == null) {
mMemoryCache.put(data, value);
}
}
//END_INCLUDE(add_bitmap_to_cache)
}
/**
* Get from memory cache.
*
* @param data Unique identifier for which item to get
* @return The bitmap drawable if found in cache, null otherwise
* @return The bitmap if found in cache, null otherwise
*/
public BitmapDrawable getBitmapFromMemCache(String data) {
//BEGIN_INCLUDE(get_bitmap_from_mem_cache)
BitmapDrawable memValue = null;
public Bitmap getBitmapFromMemCache(String data) {
Bitmap memValue = null;
if (mMemoryCache != null) {
memValue = mMemoryCache.get(data);
@@ -286,59 +184,6 @@ public class Cache {
}
return memValue;
//END_INCLUDE(get_bitmap_from_mem_cache)
}
/**
* Get from disk cache.
*
* @param data Unique identifier for which item to get
* @return The bitmap if found in cache, null otherwise
*/
public Bitmap getBitmapFromDiskCache(String data) {
//BEGIN_INCLUDE(get_bitmap_from_disk_cache)
final String key = hashKeyForDisk(data);
Bitmap bitmap = null;
synchronized (mDiskCacheLock) {
while (mDiskCacheStarting) {
try {
mDiskCacheLock.wait();
} catch (InterruptedException e) {
}
}
if (mDiskLruCache != null) {
InputStream inputStream = null;
try {
final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
if (Worker.debuggable > 0) {
Log.v(TAG, "Disk cache hit");
}
inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
if (inputStream != null) {
FileDescriptor fd = ((FileInputStream) inputStream).getFD();
// Decode bitmap, but we don't want to sample so give
// MAX_VALUE as the target dimensions
bitmap = Resizer.decodeSampledBitmapFromDescriptor(
fd, Integer.MAX_VALUE, Integer.MAX_VALUE, this);
}
}
} catch (final IOException e) {
Log.e(TAG, "getBitmapFromDiskCache - " + e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
}
}
}
return bitmap;
}
//END_INCLUDE(get_bitmap_from_disk_cache)
}
/**
@@ -389,63 +234,6 @@ public class Cache {
Log.v(TAG, "Memory cache cleared");
}
}
synchronized (mDiskCacheLock) {
mDiskCacheStarting = true;
if (mDiskLruCache != null && !mDiskLruCache.isClosed()) {
try {
mDiskLruCache.delete();
if (Worker.debuggable > 0) {
Log.v(TAG, "Disk cache cleared");
}
} catch (IOException e) {
Log.e(TAG, "clearCache - " + e);
}
mDiskLruCache = null;
// initDiskCache();
}
}
}
/**
* Flushes the disk cache associated with this Cache object. Note that this includes
* disk access so this should not be executed on the main/UI thread.
*/
public void flush() {
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null) {
try {
mDiskLruCache.flush();
if (Worker.debuggable > 0) {
Log.v(TAG, "Disk cache flushed");
}
} catch (IOException e) {
Log.e(TAG, "flush - " + e);
}
}
}
}
/**
* Closes the disk cache associated with this Cache object. Note that this includes
* disk access so this should not be executed on the main/UI thread.
*/
public void close() {
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null) {
try {
if (!mDiskLruCache.isClosed()) {
mDiskLruCache.close();
mDiskLruCache = null;
if (Worker.debuggable > 0) {
Log.v(TAG, "Disk cache closed");
}
}
} catch (IOException e) {
Log.e(TAG, "close - " + e);
}
}
}
}
/**
@@ -453,10 +241,6 @@ public class Cache {
*/
public static class CacheParams {
public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
public int diskCacheSize = DEFAULT_DISK_CACHE_SIZE;
public File diskCacheDir;
public CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
public int compressQuality = DEFAULT_COMPRESS_QUALITY;
public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
public boolean diskCacheEnabled = DEFAULT_DISK_CACHE_ENABLED;
@@ -470,7 +254,6 @@ public class Cache {
* is sufficient.
*/
public CacheParams(Context context, String diskCacheDirectoryName) {
diskCacheDir = getDiskCacheDir(context, diskCacheDirectoryName);
}
/**
@@ -592,13 +375,11 @@ public class Cache {
* onward this returns the allocated memory size of the bitmap which can be larger than the
* actual bitmap data byte count (in the case it was re-used).
*
* @param value
* @param bitmap
* @return size in bytes
*/
@TargetApi(VERSION_CODES.KITKAT)
public static int getBitmapSize(BitmapDrawable value) {
Bitmap bitmap = value.getBitmap();
public static int getBitmapSize(Bitmap bitmap) {
// From KitKat onward use getAllocationByteCount() as allocated bytes can potentially be
// larger than bitmap byte count.
if (Utils.hasKitKat()) {

View File

@@ -18,8 +18,6 @@ package org.nativescript.widgets.image;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.util.Log;
@@ -28,6 +26,7 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
@@ -37,14 +36,12 @@ import java.net.URL;
* A simple subclass of {@link Resizer} that fetch and resize images from a file, resource or URL.
*/
public class Fetcher extends Resizer {
private static final String RESOURCE_PREFIX = "res://";
private static final String FILE_PREFIX = "file:///";
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;
private DiskLruCache mHttpDiskCache;
private File mHttpCacheDir;
private DiskLruCache mHttpDiskCache;
private boolean mHttpDiskCacheStarting = true;
private final Object mHttpDiskCacheLock = new Object();
private static final int DISK_CACHE_INDEX = 0;
@@ -65,11 +62,6 @@ public class Fetcher extends Resizer {
@Override
protected void initDiskCacheInternal() {
super.initDiskCacheInternal();
initHttpDiskCache();
}
private void initHttpDiskCache() {
if (!mHttpCacheDir.exists()) {
mHttpCacheDir.mkdirs();
}
@@ -104,14 +96,12 @@ public class Fetcher extends Resizer {
}
mHttpDiskCache = null;
mHttpDiskCacheStarting = true;
// initHttpDiskCache();
}
}
}
@Override
protected void flushCacheInternal() {
super.flushCacheInternal();
synchronized (mHttpDiskCacheLock) {
if (mHttpDiskCache != null) {
try {
@@ -128,7 +118,6 @@ public class Fetcher extends Resizer {
@Override
protected void closeCacheInternal() {
super.closeCacheInternal();
synchronized (mHttpDiskCacheLock) {
if (mHttpDiskCache != null) {
try {
@@ -153,9 +142,9 @@ public class Fetcher extends Resizer {
* @param data The data to load the bitmap, in this case, a regular http URL
* @return The downloaded and resized bitmap
*/
private Bitmap processBitmap(String data, int decodeWidth, int decodeHeight) {
private Bitmap processHttp(String data, int decodeWidth, int decodeHeight) {
if (debuggable > 0) {
Log.v(TAG, "processBitmap - " + data);
Log.v(TAG, "processHttp - " + data);
}
final String key = Cache.hashKeyForDisk(data);
@@ -212,7 +201,7 @@ public class Fetcher extends Resizer {
Bitmap bitmap = null;
if (fileDescriptor != null) {
bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, decodeWidth,
decodeHeight, getImageCache());
decodeHeight, getCache());
}
if (fileInputStream != null) {
try {
@@ -223,34 +212,65 @@ public class Fetcher extends Resizer {
return bitmap;
}
private Bitmap processHttpNoCache(String data, int decodeWidth, int decodeHeight) {
if (debuggable > 0) {
Log.v(TAG, "processHttp - " + data);
}
final String key = Cache.hashKeyForDisk(data);
FileOutputStream outputStream = null;
Bitmap bitmap = null;
try {
outputStream = new FileOutputStream(key);
if (downloadUrlToStream(data, outputStream)) {
bitmap = decodeSampledBitmapFromDescriptor(outputStream.getFD(), decodeWidth, decodeHeight, getCache());
}
} catch (IOException e) {
Log.e(TAG, "processBitmap - " + e);
} catch (IllegalStateException e) {
Log.e(TAG, "processBitmap - " + e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
}
}
}
return bitmap;
}
@Override
protected Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight) {
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, "processBitmap - " + filename);
Log.v(TAG, "processFile - " + filename);
}
return decodeSampledBitmapFromFile(filename, decodeWidth,
decodeHeight, getImageCache());
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, "processBitmap - " + resId);
Log.v(TAG, "processResource - " + resId);
}
return decodeSampledBitmapFromResource(mResources, resId, decodeWidth,
decodeHeight, getImageCache());
return decodeSampledBitmapFromResource(mResources, resId, decodeWidth, decodeHeight, getCache());
} else {
Log.v(TAG, "Missing ResourceID: " + stringData);
}
} else {
return processBitmap(stringData, decodeWidth, decodeHeight);
if (useCache) {
return processHttp(stringData, decodeWidth, decodeHeight);
} else {
return processHttpNoCache(stringData, decodeWidth, decodeHeight);
}
}
} else {
Log.v(TAG, "Invalid Value: " + String.valueOf(data) + ". Expecting String or Integer.");
Log.v(TAG, "Invalid Value: " + String.valueOf(data));
}
return null;
@@ -292,7 +312,8 @@ public class Fetcher extends Resizer {
if (in != null) {
in.close();
}
} catch (final IOException e) {}
} catch (final IOException e) {
}
}
return false;
}

View File

@@ -23,9 +23,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.Log;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
@@ -37,6 +35,9 @@ import java.lang.ref.WeakReference;
*/
public abstract class Worker {
protected static final String RESOURCE_PREFIX = "res://";
protected static final String FILE_PREFIX = "file:///";
static final String TAG = "JS";
private static final int FADE_IN_TIME = 200;
@@ -78,7 +79,7 @@ public abstract class Worker {
/**
* Load an image specified by the data parameter into an ImageView (override
* {@link Worker#processBitmap(Object, int, int)} to define the processing logic). A memory and
* {@link Worker#processBitmap(Object, 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}
@@ -93,8 +94,11 @@ public abstract class Worker {
return;
}
BitmapDrawable value = null;
Bitmap value = null;
String dataString = String.valueOf(data);
if (debuggable > 0) {
Log.d(TAG, "loadImage on: " + imageView + " to: " + dataString);
}
if (mCache != null && useCache) {
value = mCache.getBitmapFromMemCache(dataString);
@@ -102,21 +106,25 @@ public abstract class Worker {
if (value == null && !async) {
// Decode sync.
Bitmap bitmap = processBitmap(data, decodeWidth, decodeHeight);
if (bitmap != null) {
value = new BitmapDrawable(mResources, bitmap);
value = processBitmap(data, decodeWidth, decodeHeight, useCache);
if (value != null) {
if (mCache != null && useCache) {
// Don't add Images loaded from Resources to disk cache.
boolean addToDiskCache = !(data instanceof Number);
mCache.addBitmapToCache(dataString, value, addToDiskCache);
if (debuggable > 0) {
Log.v(TAG, "loadImage.addBitmapToCache: " + imageView + ", src: " + dataString);
}
mCache.addBitmapToCache(dataString, value);
}
}
}
if (value != null) {
// Bitmap found in memory cache
imageView.setImageDrawable(value);
if (debuggable > 0) {
Log.d(TAG, "Set ImageBitmap on: " + imageView + " to: " + dataString);
}
imageView.setImageBitmap(value);
if (listener != null) {
Log.d(TAG, "OnImageLoadedListener on: " + imageView + " to: " + dataString);
listener.onImageLoaded(true);
}
} else if (cancelPotentialWork(data, imageView)) {
@@ -176,15 +184,15 @@ public abstract class Worker {
* 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)}
* {@link Worker#loadImage(Object, ImageView, int, int, boolean, boolean, OnImageLoadedListener)}
* @return The processed bitmap
*/
protected abstract Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight);
protected abstract Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight, boolean useCache);
/**
* @return The {@link Cache} object currently being used by this Worker.
*/
protected Cache getImageCache() {
protected Cache getCache() {
return mCache;
}
@@ -246,7 +254,7 @@ public abstract class Worker {
/**
* The actual AsyncTask that will asynchronously process the image.
*/
private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
private class BitmapWorkerTask extends AsyncTask<Void, Void, Bitmap> {
private int mDecodeWidth;
private int mDecodeHeight;
private Object mData;
@@ -271,14 +279,14 @@ public abstract class Worker {
* Background processing.
*/
@Override
protected BitmapDrawable doInBackground(Void... params) {
protected Bitmap doInBackground(Void... params) {
final String dataString = String.valueOf(mData);
if (debuggable > 0) {
Log.v(TAG, "doInBackground - starting work");
Log.v(TAG, "doInBackground - starting work: " + imageViewReference.get() + ", on: " + dataString);
}
final String dataString = String.valueOf(mData);
Bitmap bitmap = null;
BitmapDrawable drawable = null;
// Wait here if work is paused and the task is not cancelled
synchronized (mPauseWorkLock) {
@@ -289,22 +297,13 @@ public abstract class Worker {
}
}
// If the image cache is available and this task has not been cancelled by 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 try and fetch the bitmap from
// the cache
if (mCache != null && !isCancelled() && getAttachedImageView() != null
&& !mExitTasksEarly && mCacheImage) {
bitmap = mCache.getBitmapFromDiskCache(dataString);
}
// If the bitmap was not found in the cache and this task has not been cancelled by
// 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
&& !mExitTasksEarly) {
bitmap = processBitmap(mData, mDecodeWidth, mDecodeHeight);
bitmap = processBitmap(mData, mDecodeWidth, mDecodeHeight, mCacheImage);
}
// If the bitmap was processed and the image cache is available, then add the processed
@@ -312,11 +311,11 @@ public abstract class Worker {
// here, if it was, and the thread is still running, we may as well add the processed
// bitmap to our cache as it might be used again in the future
if (bitmap != null) {
drawable = new BitmapDrawable(mResources, bitmap);
if (mCache != null && mCacheImage) {
// Don't add Images loaded from Resources to disk cache.
boolean addToDiskCache = !(mData instanceof Number);
mCache.addBitmapToCache(dataString, drawable, addToDiskCache);
if (debuggable > 0) {
Log.v(TAG, "addBitmapToCache: " + imageViewReference.get() + ", src: " + dataString);
}
mCache.addBitmapToCache(dataString, bitmap);
}
}
@@ -324,35 +323,48 @@ public abstract class Worker {
Log.v(TAG, "doInBackground - finished work");
}
return drawable;
return bitmap;
}
/**
* Once the image is processed, associates it to the imageView
*/
@Override
protected void onPostExecute(BitmapDrawable value) {
protected void onPostExecute(Bitmap value) {
boolean success = false;
// if cancel was called on this task or the "exit early" flag is set then we're done
if (isCancelled() || mExitTasksEarly) {
value = null;
}
final ImageView imageView = getAttachedImageView();
if (value != null && imageView != null) {
if (debuggable > 0) {
Log.v(TAG, "onPostExecute - setting bitmap");
}
success = true;
setImageDrawable(imageView, value);
final String dataString = String.valueOf(mData);
if (debuggable > 0) {
Log.v(TAG, "onPostExecute - setting bitmap for: " + imageViewReference.get() + " src: " + dataString);
}
final ImageView imageView = getAttachedImageView();
if (debuggable > 0) {
Log.v(TAG, "onPostExecute - current ImageView: " + imageView);
}
if (value != null && imageView != null) {
success = true;
if (debuggable > 0) {
Log.d(TAG, "Set ImageDrawable on: " + imageView + " to: " + dataString);
}
imageView.setImageBitmap(value);
}
if (mOnImageLoadedListener != null) {
if (debuggable > 0) {
Log.d(TAG, "OnImageLoadedListener on: " + imageView + " to: " + dataString);
}
mOnImageLoadedListener.onImageLoaded(success);
}
}
@Override
protected void onCancelled(BitmapDrawable value) {
protected void onCancelled(Bitmap value) {
super.onCancelled(value);
synchronized (mPauseWorkLock) {
mPauseWorkLock.notifyAll();
@@ -408,31 +420,31 @@ public abstract class Worker {
}
}
/**
* Called when the processing is complete and the final drawable should be
* set on the ImageView.
*
* @param imageView
* @param drawable
*/
private void setImageDrawable(ImageView imageView, Drawable drawable) {
if (mFadeInBitmap) {
// Transition drawable with a transparent drawable and the final drawable
final TransitionDrawable td =
new TransitionDrawable(new Drawable[] {
new ColorDrawable(0),
drawable
});
// Set background to loading bitmap
imageView.setBackgroundDrawable(
new BitmapDrawable(mResources, mLoadingBitmap));
imageView.setImageDrawable(td);
td.startTransition(FADE_IN_TIME);
} else {
imageView.setImageDrawable(drawable);
}
}
// /**
// * Called when the processing is complete and the final drawable should be
// * set on the ImageView.
// *
// * @param imageView
// * @param bitmap
// */
// private void setImageDrawable(ImageView imageView, Bitmap bitmap) {
// if (mFadeInBitmap) {
// // Transition drawable with a transparent drawable and the final drawable
// final TransitionDrawable td =
// new TransitionDrawable(new Drawable[] {
// new ColorDrawable(0),
// new BitmapDrawable(bitmap)
// });
// // Set background to loading bitmap
// imageView.setBackgroundDrawable(
// new BitmapDrawable(mResources, mLoadingBitmap));
//
// imageView.setImageDrawable(td);
// td.startTransition(FADE_IN_TIME);
// } else {
// imageView.setImageBitmap(bitmap);
// }
// }
/**
* Pause any ongoing background work. This can be used as a temporary
@@ -477,30 +489,17 @@ public abstract class Worker {
}
}
protected void initDiskCacheInternal() {
if (mCache != null) {
mCache.initDiskCache();
}
}
protected void clearCacheInternal() {
if (mCache != null) {
mCache.clearCache();
}
}
protected void flushCacheInternal() {
if (mCache != null) {
mCache.flush();
}
}
protected abstract void initDiskCacheInternal();
protected void closeCacheInternal() {
if (mCache != null) {
mCache.close();
mCache = null;
}
}
protected abstract void flushCacheInternal();
protected abstract void closeCacheInternal();
public void initCache() {
new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);

View File

@@ -5,8 +5,8 @@ package org.nativescript.widgets;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
/**
* @author hhristov
@@ -29,6 +29,7 @@ public class ImageView extends android.widget.ImageView {
public void setRotationAngle(float rotationAngle) {
this.rotationAngle = rotationAngle;
invalidate();
}
public ImageView(Context context) {
@@ -140,6 +141,14 @@ public class ImageView extends android.widget.ImageView {
this.pBitmap = bm;
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
if (drawable instanceof BitmapDrawable) {
this.pBitmap = ((BitmapDrawable)drawable).getBitmap();
}
}
@Override
protected void onDraw(Canvas canvas) {
BorderDrawable background = this.getBackground() instanceof BorderDrawable ? (BorderDrawable)this.getBackground() : null;
@@ -150,6 +159,10 @@ public class ImageView extends android.widget.ImageView {
float roundedBorderWidth = (float) Math.floor(uniformBorderWidth);
float innerRadius = Math.max(0, uniformBorderRadius - roundedBorderWidth);
if (background != null) {
background.draw(canvas);
}
// The border width is included in the padding so there is no need for
// clip if there is no inner border radius.
if (innerRadius > 0) {
@@ -215,9 +228,5 @@ public class ImageView extends android.widget.ImageView {
else {
super.onDraw(canvas);
}
if (background != null) {
background.draw(canvas);
}
}
}