Android custom SimpleCursorAdapter with image from file with path in database - android

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

Pre-style the Color of a Substring in a Spinner Option [Android Studio]

I've got an SQLite database with a units table. The units table is set up with only two columns:
create table units (_id INTEGER PRIMARY KEY, desc TEXT)
Example data for a row in this table is:
_id: 4
desc: "Helix #5 [2231]"
The "[2231]" substring is important, and I'd like to change its color to a medium gray color. Id also prefer to do this to the data in the desc column, as opposed to manipulating it with java.
So, I query for the data:
/**
* Get all unit records for display in spinner
*/
public Cursor getAllUnitRecords(){
String sql = "select * from units order by `desc`";
return db.rawQuery(sql, null);
}
My spinner looks like this:
<Spinner
android:id="#+id/UnitSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dropdown" />
And I get the data to the spinner like this:
// Prepare unit dropdown
Cursor units = db.getAllUnitRecords();
MatrixCursor unitsMatrixCursor = new MatrixCursor(new String[] { "_id", "desc" });
unitsMatrixCursor.addRow(new Object[] { 0, "" });
MergeCursor unitsMergeCursor = new MergeCursor(new Cursor[] { unitsMatrixCursor, units });
String[] unitsFrom = new String[]{"desc"};
int[] unitsTo = new int[]{android.R.id.text1};
Spinner unitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
SimpleCursorAdapter unitAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_dropdown_item, unitsMergeCursor, unitsFrom, unitsTo, 0);
unitAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
unitSpinner.setAdapter(unitAdapter);
Since I'd like to color the "[2231]" substring a medium gray color, I thought I might be able to change the value of desc in the database, so that it looks like this:
"Helix #5 <font color='#6e737e'>[2231]</font>"
I did that only because I was searching the internet, and it seemed like it might work. Well, that doesn't work, as the tags are just output, instead of changing the color. What is wrong, and how can I fix it? I guess I'm open to a different solution if necessary, but this Android stuff is hard for me, as I don't work on it very often, so I was trying to go for the easiest solution.
UPDATE #1 ----------------------
So #MartinMarconcini was kind enough to point me in the right direction, and I copy and pasted his colorSpan method into my activity class to test it out. I then looked all around Stack Overflow for any clues as to how to modify the text of my spinner, and then how to modify the text that's in a SimpleCursorAdapter.
I found these questions with answers:
Android, using SimpleCursorAdapter to set colour not just strings
Changing values from Cursor using SimpleCursorAdapter
That gave me some ideas, so I tried to work with that:
// Prepare unit dropdown
Cursor units = db.getAllUnitRecords();
MatrixCursor unitsMatrixCursor = new MatrixCursor(new String[] { "_id", "desc" });
unitsMatrixCursor.addRow(new Object[] { 0, "" });
MergeCursor unitsMergeCursor = new MergeCursor(new Cursor[] { unitsMatrixCursor, units });
String[] unitsFrom = new String[]{"desc"};
int[] unitsTo = new int[]{android.R.id.text1};
Spinner unitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
SimpleCursorAdapter unitAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_dropdown_item, unitsMergeCursor, unitsFrom, unitsTo, 0);
unitAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
/* NEW CODE STARTS HERE */
unitAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View aView, Cursor aCursor, int aColumnIndex) {
if (aColumnIndex == 1) {
String desc = aCursor.getString(aColumnIndex);
TextView textView = (TextView) aView;
final Spannable colorized = colorSpan(desc);
textView.setText(TextUtils.isEmpty(colorized) ? desc + "a" : colorized + "b");
return true;
}
return false;
}
});
/* NEW CODE ENDS HERE */
unitSpinner.setAdapter(unitAdapter);
Notice I added the letter "a" if there was no text, and "b" if there was text. Sure enough, the "a" and "b" were added to my spinner items, but there was no color change! So, I am trying ... but could still use some help. Here is an image of what I'm seeing:
As mentioned in the comments, the presentation shouldn’t be tied to the logic. This is a presentation problem. You want to display a text and you want part of that text to be colored.
So, anywhere in your app where you need to display/present this text to the user, say…
someTextViewOrOtherWidget.setText(yourString);
…you’ll then have to call a method that does the coloring for you.
Example…
I’d move this code into a separate method/place for reuse and make it more re-usable by not hardcoding the [] and such,but this is how a simple example would look:
private Spannable colorSpan(final String text) {
if (TextUtils.isEmpty(text)) {
// can't colorize an empty text
return null;
}
// Determine where the [] are.
int start = text.indexOf("[");
int end = text.indexOf("]");
if (start < 0 || end < 0 || end < start) {
// can't find the brackets, can't determine where to colorize.
return null;
}
Spannable spannable = new SpannableString(text);
spannable.setSpan(
new ForegroundColorSpan(Color.BLUE)
, start
, end
, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
return spannable;
}
And you’d use it like…
String text = "Hello [123123] how are you?";
final Spannable colorized = colorSpan(text);
textView.setText(TextUtils.isEmpty(colorized) ? text : colorized);
I hope this gives you a better idea how to get started.
So, with a lot of help from #MartinMarconcini, I finally achieved what needed to be done, and so I wanted to leave "the answer" here, in case anyone else wants to see what needed to be done. I ended up making a custom cursor adapter, and although I'm still dumbfounded by the complexity of Android Studio, it works!
First, in the activity, the way SimpleCursorAdapter was being used ended up getting changed to the custom cursor adapter (which extends SimpleCursorAdapter).
These lines:
Spinner unitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
SimpleCursorAdapter unitAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_dropdown_item, unitsMergeCursor, unitsFrom, unitsTo, 0);
unitAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
unitSpinner.setAdapter(unitAdapter);
Were replaced with these lines:
Spinner customUnitSpinner = (Spinner) findViewById(R.id.UnitSpinner);
UnitSpinnerCursorAdapter customUnitAdapter = new UnitSpinnerCursorAdapter(this, R.layout.unit_spinner_entry, unitsMergeCursor, unitsFrom, unitsTo, 0);
customUnitSpinner.setAdapter(customUnitAdapter);
I put the custom cursor adapter in its own file, and I put Martin's colorSpan method in there too (for now):
package android.skunkbad.xxx;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class UnitSpinnerCursorAdapter extends SimpleCursorAdapter {
private Context context;
private int layout;
public UnitSpinnerCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
this.context = context;
this.layout = layout;
}
/**
* newView knows how to return a new spinner option that doesn't contain data yet
*/
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
super.newView(context, cursor, parent);
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
int descCol = c.getColumnIndex("desc");
String desc = c.getString(descCol);
final Spannable colorized = colorSpan(desc);
TextView unit_spinner_entry = (TextView) v.findViewById(R.id.custom_spinner_entry_desc);
if (unit_spinner_entry != null) {
unit_spinner_entry.setText(TextUtils.isEmpty(colorized) ? desc : colorized);
}
return v;
}
/**
* bindView knows how to take an existing layout and update it with the data pointed to by the cursor
*/
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
int descCol = cursor.getColumnIndex("desc");
String desc = cursor.getString(descCol);
final Spannable colorized = colorSpan(desc);
TextView unit_spinner_entry = (TextView) view.findViewById(R.id.custom_spinner_entry_desc);
if (unit_spinner_entry != null) {
unit_spinner_entry.setText(TextUtils.isEmpty(colorized) ? desc : colorized);
}
}
private Spannable colorSpan(final String text) {
if (TextUtils.isEmpty(text)) {
// can't colorize an empty text
return null;
}
// Determine where the [] are.
int start = text.indexOf("[");
int end = text.indexOf("]");
if (start < 0 || end < 0 || end < start) {
// can't find the brackets, can't determine where to colorize.
return null;
}
end++; /* Why do we even need this ? */
Spannable spannable = new SpannableString(text);
spannable.setSpan(
new ForegroundColorSpan(Color.rgb(100,100,100))
, start
, end
, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
return spannable;
}
}
Finally, I had to make a layout file for each entry in the spinner:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:id="#+id/custom_spinner_entry_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"
android:textColor="#FFFFFF"
android:layout_marginLeft="10dp" />
</LinearLayout>
Thanks Martin! It might seem like nothing to you, but it was hard for me, and I couldn't have done it without your help.
One note: I had to put end++; in your colorSpan method, because for some reason it wasn't coloring the closing bracket.

Get new random image when button is clicked - android

I am developing an app that once a button is clicked I go to a new activity where 3 random images are set to an imageView. If the user does not like the 3 images selected, they can then click a button to generate 3 new random images. I am having two problems...
I have the imageView set to a specific size but when the images show up they are sometimes smaller and are showing horizontally. I would like them to show up vertically and all as the size of the imageView. How do I change the code to do this?
I am getting the new images to show up when I click the button but I am getting an error saying that I am running out of memory after I do it a few times. How do I change the code so I stop using all my memory?
Here is my code
public ImageView shirts;
public ImageView pants;
public ImageView shoes;
Button reload;
private Bitmap currentBitmap = null;
String[] projection = new String[]{
MediaStore.Images.Media.DATA,
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.suggested_outfit);
shirts = (ImageView)findViewById(R.id.shirtImage);
pants = (ImageView)findViewById(R.id.pantsImage);
shoes = (ImageView)findViewById(R.id.shoesImage);
shirts.setImageBitmap(getImage());
pants.setImageBitmap(getImage());
shoes.setImageBitmap(getImage());
reload = (Button)findViewById(R.id.getNewOutfit);
reload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
startActivity(getIntent());
}
});
}
public Bitmap getImage () {
Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Cursor cur = getContentResolver().query(images,
projection,"", null, ""
);
final ArrayList<String> imagesPath = new ArrayList<String>();
if (cur.moveToFirst()) {
int dataColumn = cur.getColumnIndex(
MediaStore.Images.Media.DATA);
do {
imagesPath.add(cur.getString(dataColumn));
} while (cur.moveToNext());
}
cur.close();
final Random random = new Random();
final int count = imagesPath.size();
int number = random.nextInt(count);
String path = imagesPath.get(number);
currentBitmap = BitmapFactory.decodeFile(path);
return currentBitmap;
}
}
I read that it isn't good practice to reload the activity but I can't figure out a way to generate 3 new random images. I am open for any suggestions if there is a better way to do this.
Obtaining images through an onClick method would have to have
do {
String path = cur.getString(dataColumn);
if(path.contains("yourDirectory")){ //in your case, your image directory
imagesPath.add(path);
}
} while (cur.moveToNext());
within the requested intent/activity
Decoding Bitmap consumes much memory, on your code you decode Bitmap many times when click button maybe causes OutOfMemory. Try to decode all your Bitmap once times and save them to a list. When a click to generate random Bitmap, you create random index of the list Bitmap and retrieve object by that index. Hope this help!
you can use this utils: image loader
it has memory manager.and you can set imageview when image load completed.

Image in listview via database+assets

I have a database which has a name of an animal, and in an other column a sound of the animal.
The listview works fine, and now I would like to extend it with an image out of the assets.
For this I've read about how to get the assets data and use it.
An example I've tried worked fine for me.
Now I want this "assets" code (at least I think I want this) in the extended BaseAdapter class.
Unfortunately I'm doing something wrong as I can't use the getAssets() in the BaseAdapter.
The problem starts in the try-catch block: " getAssets " doesn't get recognized
Which way would I think of solving this?
Creating another class in which this assets code can run in an extended "Activity"?
Or are there better ways of showing an image via database info in a listview?
Thank you for your support in my quest to get familiar with Android / Java.
public View getView(int position, View convertView, ViewGroup parent) {
// get view reference
View view = convertView;
// if null
if(view == null) {
// inflate new layout
view = mInflater.inflate(R.layout.layout_list_item, null);
// create a holder
ViewHolder holder = new ViewHolder();
// find controls
holder.txtName = (TextView)view.findViewById(R.id.txtName);
holder.txtPicture = (ImageView)view.findViewById(R.id.txtPicture);
// set data structure to view
view.setTag(holder);
}
// get selected user info
UserInfo userInfo = mListUserInfo.get(position);
// if not null
if(userInfo != null) {
// query data structure
ViewHolder holder = (ViewHolder)view.getTag();
// set data to display
holder.txtName.setText(userInfo.getName() + ", " + userInfo.getPicture() );
try {
// get input stream
InputStream ips = getAssets().open( userInfo.getPicture() + ".jpg");
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
// set image to ImageView
holder.txtPicture.setImageDrawable( d );
}
catch(IOException ex) {
Log.e("Imageloading", "Could not load '" + ex.getMessage()+ "'!");
}
}
// return view
return view;
}
I've just edited the code.
To solve the getAssets() thing I've done the following:
holder.txtPicture.setImageDrawable( getSomePicture(null, userInfo.getPicture() + ".jpg") );
public Drawable getSomePicture(Context myContext, String WhichPicture) throws IOException {
// get input stream
InputStream ips = myContext.getAssets().open( WhichPicture );
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
return d;
}
This still is not the solution, researching some more....
Found an interesting source for Lazy Loading
to call getAssets() function in non-activity class you need reference to Context. In your updated code you called function 'getSomePicture()' and passed null to myContext parameter. that means your code will fail because you have myContext.getAssets() later in your method code.
try to do this in your getView method:
Context context = getContext();
holder.txtPicture.setImageDrawable( getSomePicture(context, userInfo.getPicture() + ".jpg") );
You won't have access to the getAssets() method because that is a method that only the Context (and it's subclass, Activity) have. What I find is easiest here is to make your BaseAdapter subclasses private inner classes of the Activity in which it is to be used. That way, you'll have access to the getAssets() method.
Here is what this might look like:
public class YourExampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
}
private class YourListAdapter extends BaseAdapter {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// get view reference
View view = convertView;
// if null
if(view == null) {
// inflate new layout
view = mInflater.inflate(R.layout.layout_list_item, null);
// create a holder
ViewHolder holder = new ViewHolder();
// find controls
holder.txtName = (TextView)view.findViewById(R.id.txtName);
holder.txtPicture = (ImageView)view.findViewById(R.id.txtPicture);
// set data structure to view
view.setTag(holder);
}
// get selected user info
UserInfo userInfo = mListUserInfo.get(position);
// if not null
if(userInfo != null) {
// query data structure
ViewHolder holder = (ViewHolder)view.getTag();
// set data to display
holder.txtName.setText(userInfo.getName() + ", " + userInfo.getPicture() );
try {
// get input stream
InputStream ips = getAssets().open( userInfo.getPicture() + ".jpg");
Log.d("Imageloading", "Reading: " + ips);
// load image as Drawable
Drawable d = Drawable.createFromStream(ips, null);
// set image to ImageView
holder.txtPicture.setImageDrawable( d );
}
catch(IOException ex) {
Log.e("Imageloading", "Could not load '" + ex.getMessage()+ "'!");
}
}
// return view
return view;
}
// Override other key methods of BaseAdapter here
}
}
Alternatively, you could take in a Context object in the constructor when the BaseAdapter subclass is instantiated, and keep a reference to that with which to call getAssets() on. Please post a comment if you need an example of this.

Display sdcard subfolder's images in gridview, and update it when changes

Hi all,
I have searched a lot for my problem, I found a lot of posts with similar problems, but no one gave me a correct solution.
What I want is a gridview displaying a sdcard folder's images. I also have to offer the possibility to take a picture, and when going back to the gridview, update it with the new picture.
To take the picture, I use this code :
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getImageFileUri());
startActivityForResult(intent, TAKE_PICTURE_ACTIVITY);
Where getImageFileUri() is a function giving me a picture name with a timestamp, using Environment.getExternalStorageDirectory() to get the sdcard path, and checking if the folder exists (and create it if it doesn't).
For the moment, I use a cursor to get my images :
private void displayGallery() {
// Query params :
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Images.Media._ID};
String selection = MediaStore.Images.Media.DATA + " like ? ";
String[] selectionArgs = {"%Otiama%"};
// Submit the query :
mCursor = managedQuery(uri, projection, selection, selectionArgs, null);
if (mCursor != null) {
mGridView.setOnItemClickListener(this);
mGridView.setAdapter(new ImageAdapter(this, mCursor));
}
else showToast("Gallery is empty : " + uri.toString());
}
And here is my adapter's getView :
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
// Move cursor to current position
mCursor.moveToPosition(position);
// Get the current value for the requested column
int columnIndex = mCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int imageID = mCursor.getInt(columnIndex);
// obtain the image URI
Uri uri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Integer.toString(imageID) );
String url = uri.toString();
// Set the content of the image based on the image URI
int originalImageId = Integer.parseInt(url.substring(url.lastIndexOf("/") + 1, url.length()));
Bitmap b = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), originalImageId, MediaStore.Images.Thumbnails.MINI_KIND, null);
imageView.setImageBitmap(b);
return imageView;
}
This code works, but is very slow and doesn't update (new pictures's thumbnails aren't created), even if I call again my displayGallery() function in the onActivityResult(). Well, it doesn't update even if I reload the app >< . I have to run it again from eclipse.
In fact, what I would like is the same behavior than ES File Explorer (when you open a folder, the pictures have all a preview image, and they are loaded asynchronously), which, I think, doesn't use those ** thumbnails.
So I tried to load the pictures as bitmaps using the bitmap factory, but even with a few pictures (1-2), I instantly get a "java.lang.OutOfMemoryError: bitmap size exceeds VM budget"... I guess I have to resize them, but if I do so, won't I have the same error when I will load 20-30 pictures ?? Or the problem is that each picture exceeds the budget, and so if I resize them I will avoid this error for all of them ?
Sorry for the big post, if someone can help...
Well, I answer myself :
I created my own thumbnails this way :
public static Bitmap resizedBitmap(Bitmap bitmap, int newWidth) {
// Get the bitmap size :
int width = bitmap.getWidth();
int height = bitmap.getHeight();
double ratio = (double)width / (double)height;
// Compute the thumbnail size :
int thumbnailHeight = newWidth;
int thumbnailWidth = (int) ( (double)thumbnailHeight * ratio);
// Create a scaled bitmap :
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, thumbnailWidth, thumbnailHeight, false);
return scaledBitmap;
}
I don't use cursors anymore, to load the thumbnails I proceed like this (in the ImageAdapter) :
public void loadThumbnails() {
// Init the ArrayList :
_thumbnails = new ArrayList<ImageView>();
_imagesNames = new ArrayList<String>();
// Run through the thumbnails dir :
File imagesThumbnailsDir = new File(_imagesThumbnailsDirUri.getPath());
File[] imagesThumbnails = imagesThumbnailsDir.listFiles();
Arrays.sort(imagesThumbnails);
// For each thumbnail :
for(File imageThumbnail : imagesThumbnails)
{
// Check if the image exists :
File image = new File(_imagesDirUri.getPath() + File.separator + imageThumbnail.getName());
if(image.exists()) {
ImageView imageView = new ImageView(_context);
imageView.setImageDrawable(Drawable.createFromPath(imageThumbnail.getAbsolutePath()));
_thumbnails.add(imageView);
_imagesNames.add(imageThumbnail.getName());
}
// If not, delete the thumbnail :
else {
ImageUtils.deleteFile(Uri.fromFile(imageThumbnail));
}
}
}
And so my ImageAdapter's getView function sounds like this :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(_context);
imageView.setLayoutParams(new GridView.LayoutParams(80, 80));
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageDrawable(_thumbnails.get(position).getDrawable());
return imageView;
}
Hope it helps..

How to store images in sqlite database and display them?

In my application I need to display questions along with images in Android. I have stored questions in sqlite db and displaying them. I have created a folder DBimages in assets folder. Now all my images are there in DBimages folder.
I have created two classes. I am able to display the question and options from local sqlite db but I am unable to display the image which is stored in assets\DBimages\abc.png.
The names of all the images are stored in pictures column in sqlite db table. But how to display the image related to that particular question. Please help me regarding this.
UserBO.java
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getImage() {
return pictures;
}
public void setImage(String pictures) {
this.pictures = pictures;
}
Select.java
ArrayList<UserBO> usersList = new ArrayList<UserBO>();
for (int i = 0; i < stringList.size(); i++) {
ArrayList<String> list = stringList.get(i);
UserBO user = new UserBO();
try {
user.id = Integer.parseInt(list.get(0));
user.name = list.get(2);
user.question = list.get(3);
user.option1 = list.get(4);
user.option2 = list.get(5);
user.option3 = list.get(6);
user.option4 = list.get(7);
user.option5 = list.get(8);
user.pictures = list.get(9);
user.answer = (list.get(10));
}
catch (Exception e){}
public View getView(int position, View convertView, ViewGroup parent){
View view = convertView;
try{
if (view == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.list_item, null);
}
final UserBO listItem = mList.get(position);
if (listItem != null) {
( (TextView) view.findViewById(R.id.tv_question) ).setText( listItem.getQuestion()+"");
((ImageView) view.findViewById(R.id.imageView1)).setImageResource(R.drawable.f);
( (RadioButton) view.findViewById(R.id.option1) ).setText( listItem.getOption1() );
( (RadioButton) view.findViewById(R.id.option2) ).setText( listItem.getOption2()+"" );
( (RadioButton) view.findViewById(R.id.option3) ).setText( listItem.getOption3()+"" );
( (RadioButton) view.findViewById(R.id.option4) ).setText( listItem.getOption4()+"" );
( (RadioButton) view.findViewById(R.id.option5) ).setText( listItem.getOption5()+"" );
}}catch(Exception e){}
return view;
}
}
Some ugly way, to store images in SQLite database. Just store images in sdcard and save the relevant path of those images in SQLite database.
In your need, put the images in either drawable folder or in asset folder then store its
name(with question ID or number also) in SQLite and use it in application.
I had the same problem and i want first to clarify this, storing image path or uri from gallery as string might not give good results when populating or display the data to listview specific the images. You need to store your images on sqlite database as BLOB data types and you need to insert the image to the DB as bytes.
In my case, i tried working with image path from gallery but never worked from me.
Image path is suitable when only your are using urls from a website
You can see an example from this link

Categories

Resources