6

I am using NetworkImageView to show some covers downloaded from a remote URL and I successfully manage to cache and show them, but I want to let users set their own cover images if they want. I tried to use setImageUrl method with Uri.fromFile(mCoverFile).toString() as arguments, but it doesn't work. Since it is a mix of remote and local images I can't switch to regular ImageViews, so I was wondering if there's any way to enable loading of local images.

I am of course aware of the ImageView's setImageBitmap method, but NetworkImageView automatically resizes the created Bitmap and also prevents View recycling in GridViews and ListViews.

UPDATE: njzk2's answer did the trick. To autoresize the Bitmap according to your View size, then just copy the ImageRequest.doParse method from Volley's source.

Community
  • 1
  • 1
Vektor88
  • 4,841
  • 11
  • 59
  • 111

3 Answers3

7

NetworkImageView uses ImageLoader, which in turn uses an ImageCache.

You can provide a custom ImageCache with your images, provided you use the same mechanism for keys:

 return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
            .append("#H").append(maxHeight).append(url).toString();

url is not tested before the actual request would be done, so no issue here.

Typically, your 'cache' could look like :

public class MyCache implements ImageLoader.ImageCache {

    @Override
    public Bitmap getBitmap(String key) {
        if (key.contains("file://")) {
            return BitmapFactory.decodeFile(key.substring(key.indexOf("file://") + 7));
        } else {
            // Here you can add an actual cache
            return null;
        }
    }
    @Override
    public void putBitmap(String key, Bitmap bitmap) {
        // Here you can add an actual cache
    }
}

You use it like :

imageView.setImageUrl(Uri.fromFile(mCoverFile).toString(), new MyCache());

(This has not been actually tested and there may be some adjustments to do)

njzk2
  • 38,969
  • 7
  • 69
  • 107
  • The problem of this approach, that I already tried, is that I get an error: `FileURLConnection cannot be cast to HttpURLConnection`. – Vektor88 Mar 17 '14 at 21:00
  • (that being said, the NetworkImageView does simply call `setImageBitmap(response.getBitmap());` once the request is done. Once this is done, there is nothing actually preventing the view recycling that I can see) – njzk2 Mar 17 '14 at 21:02
  • My approach does not reach the connection opening. If you look closely, you'll see that the cache always hits, and therefore the connection is never opened. – njzk2 Mar 17 '14 at 21:03
  • 1
    That's because of the structure of the cache key. It starts with the size of the image. – njzk2 Mar 17 '14 at 21:12
  • 1
    Note that this will decode the bitmap on the UI thread, which is not advisable. – goncalossilva Apr 21 '14 at 13:47
  • I agree with @goncalossilva's comment, ImageCache's purpose is store bitmaps in the memory so ImageLoader can fetch it fast, finally achieved serve Bitmap without any background thread execution, hence getBitmap() should be doing as light-weight as possible work, if we want to load a bitmap from file system, we should perform a new Request. – VinceStyling Aug 12 '14 at 05:50
  • @VinceStyling: agreed, but there is no way to tell ImageLoader to use a custom ImageRequest, so the new Request would not work for local files (at least at the time of the answer it did not, I have not checked it in a while) – njzk2 Aug 12 '14 at 12:46
1

Thank you for your answer. I wrote some code based on your help.

usage: just use LocalImageCache.class as Cache. No more code to change.

private ImageLoader mLocalImageLoader;

mLocalImageLoader = new ImageLoader(mRequestQueue,
            new LocalImageCache(mCtx));

NetworkImageView test = (NetworkImageView) findViewById(R.id.iv_test);  

test.setImageUrl("/storage/emulated/0/DCIM/Philm/2017_03_24_01_.png", MySingleton.getInstance(this.getApplicationContext()).getLocalImageLoader());

public class LocalImageCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {

public LocalImageCache(int maxSize) {
    super(maxSize);
}

public LocalImageCache(Context ctx) {
    this(getCacheSize(ctx));
}

@Override
public Bitmap getBitmap(String key) {
    key = key.substring(key.indexOf("/"));
    Bitmap result = get(key);
    Log.d("TAG", key);
    if (result == null) {
        Bitmap temp =  BitmapFactory.decodeFile(key);
        put(key, temp);
        return temp;
    } else {
        return result;
    }
}

@Override
public void putBitmap(String key, Bitmap bitmap) {
    // Here you can add an actual cache
    // Never touch here
}

// 默认屏幕5倍的图片缓存
// Returns a cache size equal to approximately three screens worth of images.
public static int getCacheSize(Context ctx) {
    final DisplayMetrics displayMetrics = ctx.getResources().
            getDisplayMetrics();
    final int screenWidth = displayMetrics.widthPixels;
    final int screenHeight = displayMetrics.heightPixels;
    // 4 bytes per pixel
    final int screenBytes = screenWidth * screenHeight * 4;

    return screenBytes * 5;
}

@Override
protected int sizeOf(String key, Bitmap value) {
    return value.getRowBytes() * value.getHeight();
}

}

CoderYel
  • 228
  • 2
  • 8
0

NetworkImageView extends ImageView. You should be able to use the same methods as a regular ImageView

image.setImageResource(R.drawable.my_image);

or

imageView.setImageBitmap(BitmapFactory.decodeFile(imagePath)); 
MobileMon
  • 8,341
  • 5
  • 56
  • 75
  • Read the full question: local files, not drawables. I'd like to take advantage of all `NetworkImageView` features, bitmap autoresizing, prevention of recycled images in grids/lists and so on. – Vektor88 Mar 17 '14 at 20:40
  • a drawable is a local file, I don't understand? – MobileMon Mar 17 '14 at 20:41
  • A `Drawable` is a resource image inside your /res folder. A local file is a saved jpeg image or one transferred via USB, for example. – Vektor88 Mar 17 '14 at 20:42
  • See my updated answer, you just use the normal route with any old ImageView since NetworkImageView is in fact an ImageView – MobileMon Mar 17 '14 at 20:47
  • You don't use the regular route, because when you use NetworkImageView it also autoresizes the Bitmap so that it takes less memory and using regular imageview methods doesn't prevent view recycling in GridViews and ListViews – Vektor88 Mar 17 '14 at 20:49