Image cache won't work if FileSystem is in read-only mode. (#66)

ImageView will remove its bitmap when not in visual tree and apply it back once shown.
ImageView add setUri method.
ViewHelper added new method for API 21.
This commit is contained in:
Hristo Hristov
2016-11-02 16:47:36 +02:00
committed by GitHub
parent 78183d4328
commit 8feec8982b
7 changed files with 209 additions and 38 deletions

View File

@@ -142,6 +142,12 @@ public class Cache {
return bitmapSize == 0 ? 1 : bitmapSize;
}
};
} else {
clearCache();
if (mReusableBitmaps != null) {
mReusableBitmaps.clear();
mReusableBitmaps = null;
}
}
}
@@ -236,6 +242,7 @@ public class Cache {
Log.v(TAG, "Memory cache cleared");
}
}
mMemoryCache = null;
}
/**
@@ -246,18 +253,6 @@ public class Cache {
public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
public boolean diskCacheEnabled = DEFAULT_DISK_CACHE_ENABLED;
/**
* Create a set of image cache parameters that can be provided to
* {@link Cache#getInstance(CacheParams)}.
*
* @param context A context to use.
* @param diskCacheDirectoryName A unique subdirectory name that will be appended to the
* application cache directory. Usually "cache" or "images"
* is sufficient.
*/
public CacheParams(Context context, String diskCacheDirectoryName) {
}
/**
* Sets the memory cache size based on a percentage of the max available VM memory.
* Eg. setting percent to 0.2 would set the memory cache to one fifth of the available

View File

@@ -23,11 +23,14 @@ import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -47,14 +50,22 @@ public class Fetcher extends Resizer {
private static final int DISK_CACHE_INDEX = 0;
private final String mPackageName;
private static Fetcher instance;
public static Fetcher getInstance(Context context) {
if (instance == null) {
instance = new Fetcher(context);
}
return instance;
}
/**
* Initialize providing a target image width and height for the processing images.
*
* @param context
*/
public Fetcher(Context context) {
private Fetcher(Context context) {
super(context);
mHttpCacheDir = Cache.getDiskCacheDir(context, HTTP_CACHE_DIR);
mPackageName = context.getPackageName();
@@ -184,9 +195,9 @@ public class Fetcher extends Resizer {
fileDescriptor = fileInputStream.getFD();
}
} catch (IOException e) {
Log.e(TAG, "processBitmap - " + e);
Log.e(TAG, "processHttp - " + e);
} catch (IllegalStateException e) {
Log.e(TAG, "processBitmap - " + e);
Log.e(TAG, "processHttp - " + e);
} finally {
if (fileDescriptor == null && fileInputStream != null) {
try {
@@ -216,19 +227,17 @@ public class Fetcher extends Resizer {
if (debuggable > 0) {
Log.v(TAG, "processHttp - " + data);
}
final String key = Cache.hashKeyForDisk(data);
FileOutputStream outputStream = null;
ByteArrayOutputStreamInternal outputStream = null;
Bitmap bitmap = null;
try {
outputStream = new FileOutputStream(key);
outputStream = new ByteArrayOutputStreamInternal();
if (downloadUrlToStream(data, outputStream)) {
bitmap = decodeSampledBitmapFromDescriptor(outputStream.getFD(), decodeWidth, decodeHeight, getCache());
bitmap = decodeSampledBitmapFromByteArray(outputStream.getBuffer(), decodeWidth, decodeHeight, getCache());
}
} catch (IOException e) {
Log.e(TAG, "processBitmap - " + e);
} catch (IllegalStateException e) {
Log.e(TAG, "processBitmap - " + e);
Log.e(TAG, "processHttpNoCache - " + e);
} finally {
if (outputStream != null) {
try {
@@ -263,7 +272,7 @@ public class Fetcher extends Resizer {
Log.v(TAG, "Missing Image with resourceID: " + stringData);
}
} else {
if (useCache) {
if (useCache && mHttpDiskCache != null) {
return processHttp(stringData, decodeWidth, decodeHeight);
} else {
return processHttpNoCache(stringData, decodeWidth, decodeHeight);
@@ -328,4 +337,10 @@ public class Fetcher extends Resizer {
System.setProperty("http.keepAlive", "false");
}
}
private static class ByteArrayOutputStreamInternal extends ByteArrayOutputStream {
public byte[] getBuffer() {
return buf;
}
}
}

View File

@@ -23,6 +23,7 @@ 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
@@ -149,6 +150,34 @@ public abstract class Resizer extends Worker {
return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
}
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

View File

@@ -5,9 +5,11 @@ package org.nativescript.widgets;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import org.nativescript.widgets.image.Fetcher;
import org.nativescript.widgets.image.Worker;
/**
* @author hhristov
*
@@ -23,6 +25,16 @@ public class ImageView extends android.widget.ImageView {
private float rotationAngle;
private Matrix mMatrix;
private Bitmap mBitmap;
private String mUri;
private int mDecodeWidth;
private int mDecodeHeight;
private boolean mUseCache;
private boolean mAsync;
private Worker.OnImageLoadedListener mListener;
private boolean mAttachedToWindow = false;
public float getRotationAngle() {
return rotationAngle;
}
@@ -38,6 +50,23 @@ public class ImageView extends android.widget.ImageView {
this.setScaleType(ScaleType.FIT_CENTER);
}
@Override
protected void onAttachedToWindow() {
mAttachedToWindow = true;
super.onAttachedToWindow();
this.loadImage();
}
@Override
protected void onDetachedFromWindow() {
mAttachedToWindow = false;
super.onDetachedFromWindow();
if (mUri != null) {
// Clear the bitmap as we are not in the visual tree.
this.setImageBitmap(null);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
@@ -132,21 +161,30 @@ public class ImageView extends android.widget.ImageView {
}
}
private Matrix mMatrix;
private Bitmap pBitmap;
public void setUri(String uri, int decodeWidth, int decodeHeight, boolean useCache, boolean async, Worker.OnImageLoadedListener listener) {
mUri = uri;
mDecodeWidth = decodeWidth;
mDecodeHeight = decodeHeight;
mUseCache = useCache;
mAsync = async;
mListener = listener;
if (mAttachedToWindow) {
loadImage();
}
}
private void loadImage() {
Fetcher fetcher = Fetcher.getInstance(this.getContext());
if (mUri != null && fetcher != null) {
// Get the Bitmap from cache.
fetcher.loadImage(mUri, this, mDecodeWidth, mDecodeHeight, mUseCache, mAsync, mListener);
}
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
this.pBitmap = bm;
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
if (drawable instanceof BitmapDrawable) {
this.pBitmap = ((BitmapDrawable)drawable).getBitmap();
}
this.mBitmap = bm;
}
@Override
@@ -185,8 +223,8 @@ public class ImageView extends android.widget.ImageView {
float viewWidth = this.getWidth() - (2 * roundedBorderWidth);
float viewHeight = this.getHeight() - (2 * roundedBorderWidth);
float bitmapWidth = (float) pBitmap.getWidth();
float bitmapHeight = (float) pBitmap.getHeight();
float bitmapWidth = (float) mBitmap.getWidth();
float bitmapHeight = (float) mBitmap.getHeight();
float scaleX;
float scaleY;
@@ -223,7 +261,7 @@ public class ImageView extends android.widget.ImageView {
matrix.postRotate(rotationDegree);
matrix.postTranslate(viewWidth / 2 + roundedBorderWidth, viewHeight / 2 + roundedBorderWidth);
canvas.drawBitmap(this.pBitmap, matrix, null);
canvas.drawBitmap(this.mBitmap, matrix, null);
}
else {
super.onDraw(canvas);

View File

@@ -373,5 +373,21 @@ public class ViewHelper {
view.setZ(value);
}
}
@TargetApi(21)
public static float getLetterspacing(android.widget.TextView textView) {
if (ViewHelper.version >= 21) {
return textView.getLetterSpacing();
}
return 0;
}
@TargetApi(21)
public static void setLetterspacing(android.widget.TextView textView, float value) {
if (ViewHelper.version >= 21) {
textView.setLetterSpacing(value);
}
}
}