android


Android custom SimpleCursorAdapter with image from file with path in database


Gist: custom adapter gets file resource indirectly via filepath in database. Inefficiency / memory concerns. Your opinion requested.
References to all searches, related links, and things useful re topic are at post bottom.
Code below works, but several factors are of concern. Need some more experienced eyes on this please to suggest improvement or potential errors to avoid. App doesn't need to be a content provider (data sourced local to app only). The ListView in question will be very light weight with only about 5 to max 10 entries. (I left out the database stuff because it works.)
Overview:
DataBase contains some text and an Image File path. - OK
image files are stored on device (SD card / external storage, where ever). - OK
That the files are not in the database makes this different than a normal SimpleCursorAdapter - have to pull the image file. Added overhead of making it into a thumbnail before populating the listview.
As said, it's light, however, even with only one or two entries, the VM is burping. I suspect it's all the memory joggling related to the Bitmaps:
08-27 19:53:14.273: I/dalvikvm-heap(11900): Grow heap (frag case) to 4.075MB for 1228816-byte allocation
08-27 19:53:14.393: D/dalvikvm(11900): GC_CONCURRENT freed <1K, 5% free 4032K/4244K, paused 13ms+3ms, total 116ms
/* myTextAndImageCursorAdapter.java */
import android.widget.SimpleCursorAdapter;
//import android.support.v4.widget.SimpleCursorAdapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.database.Cursor;
import java.io.File;
import android.widget.ImageView;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import static android.media.ThumbnailUtils.extractThumbnail;
public class TextAndImageCursorAdapter extends SimpleCursorAdapter {
private Context context;
private int layout;
public TextAndImageCursorAdapter (Context ctx, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.context = ctx;
this.layout = layout;
}
#Override
public View newView(Context ctx, Cursor cursor, ViewGroup parent) {
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(ctx);
View vView = inflater.inflate(layout, parent, false);
int iCol_Text = c.getColumnIndex(DBCOL_TEXT);
int iCol_Image = c.getColumnIndex(DBCOL_IMAGE);
String sText = c.getString(iCol_Text);
String sFileAndPath_Image = c.getString (iCol_Image); //// sImage path & file
TextView tvText = (TextView) v.findViewById(R.id.gui_text);
if (tvText != null) {
tvText.setText(sSomeText);
}
ImageView ivImage (ImageView) v.findViewById(R.id.gui_image);
if (ivImage != null) {
ivImage.setImage (mySetImage (sFileAndPath_Image) );
}
return vView;
}
#Override
public void bindView(View v, Context ctx, Cursor c) {
//// ( like newView(), without an inflater, view, or return )
int iCol_Text = c.getColumnIndex(DBCOL_TEXT);
int iCol_Image = c.getColumnIndex(DBCOL_IMAGE);
String sText = c.getString(iCol_Text);
String sFileAndPath_Image = c.getString (iCol_Image); //// path & file
TextView tvText = (TextView) v.findViewById(R.id.gui_text);
if (tvText != null) {
tvText.setText(sSomeText);
}
ImageView ivImage (ImageView) v.findViewById(R.id.gui_image);
if (ivImage != null) {
ivImage.setImageBitmap ( mySetImage ( sFileAndPath_Image ) ) ;
}
}
/////
/////
protected Bitmap mySetImage ( String path ) {
int width = 60; int height = 40 ;
File imgFile = new File ( path ); //// usually like: \sdcard0\wherever\filename1234.bmp
Bitmap myBitmap = null;
if( imgFile.exists() )
{
myBitmap = BitmapFactory.decodeFile ( imgFile.getAbsolutePath () );
}
else
Log.d ("oops", "no image file ... using default.");
myBitmap = getTheDefaultImage (); //// not shown - this is arbitrary
}
imgFile.close();
return ( extractThumbnail ( myBitmap, width, height ) ) ;
}
}
[EDIT - added links]
Search Criteria:
"
Android custom simplecursoradapter with image from file with path in database
"
The nearest hit, but attempts to pull image from res, not from external / sd store (unanswered):
SimpleCursorAdapter how to show an image?
Also a near hit / miss - similar algo (unanswered) references 4):
Customizing list shown from SimpleCursorAdapter using ViewBinder
Almost, but OP's code doesn't work, (no working answer):
Load Image in a custom list by SimpleCursorAdapter
Working for OP, but uses JSON for remote retrieval, not local (maybe this could be tweaked, but it's not clear to me how).
How to show images in imageview in simple adapter?
Not quite, but again close:
ListView scroll slow while loading image from Internal Storage
Image Loader problems (references 2):
Imageloader not loading image on real device
Related links:
Android Custom Cursor Adapter
Android: Issue with newView and bindView in custom SimpleCursorAdapter
Similarly named hits, but unrelated to my specific questions - these usually refer to in-app RESources:
show image from database where you saved the path of image
Custom SimpleCursorAdapter error
Custom SimpleCursorAdapter, database query and NullPointerException
nullPointerException with extended SimpleCursorAdapter
Android SimpleCursorAdapter - Adding conditional images
External References:
0) Simple intro tut on custom cursor adapters
http://thinkandroid.wordpress.com/2010/01/11/custom-cursoradapters/
1) Romain Guy - basic layout ... 2 txts, 1 image
http://www.curious-creature.org/2009/02/22/android-layout-tricks-1/
2) AQuery (Android Query)
http://code.google.com/p/android-query/wiki/ImageLoading
3) Android thumbnails
http://developer.android.com/reference/android/media/ThumbnailUtils.html
4) Cust. listview with an "on/off" star image:
http://enjoyandroid.wordpress.com/2012/03/12/customizing-simple-cursor-adapter/
Two things you can do:
1) Use the ViewHolder pattern, cache the LayoutInfalter and most important: don't bind data twice:
/* ... imports */
import static android.media.ThumbnailUtils.extractThumbnail;
public class TextAndImageCursorAdapter extends SimpleCursorAdapter {
private LayoutInflater mLayoutInflater;
private Context context;
private int layout;
private class ViewHolder {
TextView textView;
ImageView imageView;
ViewHolder(View v) {
textView = (TextView) v.findViewById(R.id.gui_text);
imageView = (ImageView) v.findViewById(R.id.gui_image);
}
}
public TextAndImageCursorAdapter (Context ctx, int layout, Cursor c, String[] from, int[] to) {
super(ctx, layout, c, from, to);
this.context = ctx;
this.layout = layout;
mLayoutInflater = LayoutInflater.from(ctx);
}
#Override
public View newView(Context ctx, Cursor cursor, ViewGroup parent) {
View vView = mLayoutInflater.inflate(layout, parent, false);
vView.setTag( new ViewHolder(vView) );
// no need to bind data here. you do in later
return vView;// **EDITED:**need to return the view
}
#Override
public void bindView(View v, Context ctx, Cursor c) {
// you might want to cache these too
int iCol_Text = c.getColumnIndex(DBCOL_TEXT);
int iCol_Image = c.getColumnIndex(DBCOL_IMAGE);
String sText = c.getString(iCol_Text);
String sFileAndPath_Image = c.getString (iCol_Image); //// path & file
ViewHolder vh = (ViewHolder) v.getTag();
vh.textView.setText(sSomeText);
vh.imageView.setImageBitmap ( mySetImage ( sFileAndPath_Image ) );
}
}
2) This is really important: don't create a thumbnail on every bind. you need to cache the result:
private void setThumbnail(String path, Bitmap b) {
// save thumbnail to some kind of cache
// see comment below
}
private Bitmap getThumbnail(String path) {
Bitmap thumbnail = null;
// try to fetch the thumbnail from some kind of cache
// see comment below
return thumbnail;
}
protected Bitmap mySetImage ( String path ) {
int width = 60; int height = 40 ;
Bitmap thumbnail = getThumbnail(path); // try to fetch thumbnail
if (thumbnail != null) return thumbnail;
File imgFile = new File ( path ); //// usually like: /sdcard/wherever/filename1234.bmp
Bitmap myBitmap = null;
if( imgFile.exists() ) {
myBitmap = BitmapFactory.decodeFile ( imgFile.getAbsolutePath () );
} else {
Log.d ("oops", "no image file ... using default.");
myBitmap = getTheDefaultImage (); //// not shown - this is arbitrary
}
imgFile.close();
thumbnail = extractThumbnail ( myBitmap, width, height );
myBitmap.recycle();
setThumbnail(path, thumbnail); // save thumbnail for later reuse
return thumbnail;
}
Depending on you use case, you want to fill getThumbnail() and setThumbnail() with some kind of LruCache:
There is a in memory LruCache available in the android API and in support lib: https://developer.android.com/reference/android/util/LruCache.html
Jake made an persistent DiskLruCache: https://github.com/JakeWharton/DiskLruCache
EDIT :
#Override
public View newView(Context ctx, Cursor cursor, ViewGroup parent) {
View vView = mLayoutInflater.inflate(layout, parent, false);
vView.setTag( new ViewHolder(vView) );
// no need to bind data here. you do in later
return vView;// **EDITED:**need to return the view
}

Related Links

ADB unauthorized, having trouble connecting
How to shrink padding/ margin top and bottom of TextInputLayout error text?
Cannot resolve method: CameraSource.Builder.setAutoFocusEnabled
How to gracefully switch between networks in WebRTC?
JSON Data not getting entered in Database
how to fix this crash : java.lang.IllegalArgumentException: Each element of 'value' must be a valid managed object
Android: Reading a file from SD card and display on image view giving error
Block certain road in graphhopper
How to detect the end of scroll of a webivew
How to pass arguments to Espresso UI Tests from command line using ADB?
How to fix FATAL EXCEPTION: main android.content.res.Resources$NotFoundException
Firebase LatLng is missing a constructor with no arguments
Storing volley request for using it afterwards
What is mergeDrawableStates()?
TransactionTooLargeException when it pass one value string between activities
Create server for android application

Categories

HOME
jpa
hyperlink
time-complexity
webdriver
jax-rs
serialization
fftw
alsa
boto3
heap
github-enterprise
jxl
3nf
internationalization
alpacajs
feathersjs
chargify
google-picker
foreign-keys
sse
solrcloud
jquery-ias
string-concatenation
easeljs
fat
gpio
datastax-enterprise-graph
user-defined-functions
send
complex-numbers
hard-drive
instantiation
getpixel
sharding
getlasterror
latitude-longitude
gdata
loading
chef-solo
spring-mvc-test
apache-stanbol
laravel-elixir
opencmis
dynamic-linq
gitlist
robomongo
insight
scalding
vcloud-director-rest-api
google-cloud-console
iotivity
spring-cloud-feign
scaffolding
embedding
omniauth-facebook
deep
angular-ui-typeahead
spreadjs
maybe
dulwich
omnifaces
diff3
grouping-sets
contrast
spy++
high-resolution
solace-mq
onmousemove
floating-point-exceptions
entity-attribute-value
htdocs
filemerge
pylucene
collectionviewsource
mtu
web-garden
interop-domino
software-lifecycle
ifilter
appliance

Resources

Encrypt Message