I am populating a ListView
with a customized cursor adapter that receives a cursor result set from a database. It can take up to a 10-12 seconds to display just 20 views!
Here is the CursorAdater class where the cursor supplies Strings and a base 64 Image representation to a series of Text-views and a Image-view for each view:
public void bindView(View v, Context context, Cursor c) {
String diveSite = c.getString(c.getColumnIndexOrThrow(diveDataBase.KEY_DIVESITE));
String date = c.getString(c.getColumnIndexOrThrow(diveDataBase.KEY__DIVEDATE));
String diveNumber= c.getString(c.getColumnIndexOrThrow(diveDataBase.KEY__DIVENUMBER));
String diveImagePath = c.getString(c.getColumnIndex(diveDataBase.KEY_DIVEPICTURE));
String rating = c.getString(c.getColumnIndexOrThrow(diveDataBase.KEY_DIVERATING));
c.moveToLast();
noOfRows = Integer.parseInt(c.getString(c.getColumnIndex(diveDataBase.KEY__DIVENUMBER)));
/**
* Next set the dive site name
*/
TextView title_text = (TextView) v.findViewById(R.id.tv_DiveSiteListView);
if (title_text!= null) {
title_text.setText(diveSite);
}
/**
* Set Date of dive in smaller textView font
*/
TextView date_text = (TextView) v.findViewById(R.id.tv_diveDateList);
if (date_text!= null) {
date_text.setText(date);
}
/**
* Display the dive number in larger red font
*/
TextView dive_no = (TextView) v.findViewById(R.id.tv_diveNumberListView);
if (diveNumber!= null ) {
dive_no.setText(diveNumber+"/"+noOfRows);//+1 as rows start at zero
}
/*
* Display the rating of the dive
*/
RatingBar bar = (RatingBar) v.findViewById(R.id.ratingBarListView);
bar.setNumStars(5);
bar.setRating( Float.parseFloat(rating));
/**
* Display the image only of image not null
* First get image from Strimg pathname as a file, then convert to Bitmap and resize
*
*/
ImageView displayImage = (ImageView) v.findViewById(R.id.iv_list_image);
//set image here once taken form external string path, and resized bitmap conversion
imagePathFile = new File(diveImagePath);
try {
FileInputStream streamIn = new FileInputStream(imagePathFile);
Bitmap bitmap = BitmapFactory.decodeStream(streamIn);//retrun null if can't convert
if(bitmap!=null){
displayImage.setBackground(null);
resizedImage = reSizeImage(bitmap);
displayImage.setImageBitmap(resizedImage);
}else{
displayImage.setBackgroundResource(R.drawable.logdive3);
}
}
catch (FileNotFoundException e) {
e.printStackTrace();
}//end try catch
}//end newView
And in a List-view class the cursor is obtained in a do-in-background method of a Async Class and the adapter is set in the on-post-execute:
@Override
protected Cursor doInBackground(Void... params) {
// get the cursor from database
ViewListOfDives.data = new diveDataBase(ViewListOfDives.this);
ViewListOfDives.data.open();
// get cursor object holding all data, use a asynch inner class to load
cursor = data.getCursorData();
//ViewListOfDives.data.close();
return cursor;
}
//set the adapter to list view
@Override
protected void onPostExecute(Cursor cursor) {
//check if data available
if(cursor!=null && cursor.getCount()>0){
// get customised array adoater list
adapter = new ItemAdapter(ViewListOfDives.this, cursor);
}else{
//display o dives in data base message and finish this activity
displayDialog();
}
ViewListOfDives.this.setListAdapter(adapter);
ViewListOfDives.data.close();
super.onPostExecute(cursor);
// dispose dialog
if(pd.isShowing()){
pd.dismiss();
}
}
Have been researching allot on line an can't find much on performance optimizing cursor adapters so Any input would be much appreciated!!
Edit: including method I use to resize the images:
public Bitmap reSizeImage(Bitmap bitmapImage) {
// resize bitmap image passed and rerun new one
Bitmap resizedImage = null;
float factorH = h / (float) bitmapImage.getHeight();
float factorW = w / (float) bitmapImage.getWidth();
float factorToUse = (factorH> factorW)? factorW : factorH;
try {
resizedImage = Bitmap.createScaledBitmap(bitmapImage,
(int) (bitmapImage.getWidth() * factorToUse),
(int) (bitmapImage.getHeight() * factorToUse), false);
} catch (IllegalArgumentException e) {
Log.d(TAG, "Problem resizing Image @Line 510+");
e.printStackTrace();
}
Log.d(TAG,
"in resixed, value of resized image: "
+ resizedImage.toString());
return resizedImage;
}// end reSize
Where h and w :
// for image resizing
static int w = 250;
static int h = 280;
EDIT: Have wriiten a asynch class to handle conversion of base 64 to to bitmap, then in post execute image view is set to the bitmap. This inner class is called form the bindView method of cursoradpter class. Issue is now only the last view is populated wit an image!!
//asynch class to load nase 64 image and convert to bitmap and set imageview
private class getBitmapImage extends AsyncTask<String, Void, Bitmap>{
@Override
protected Bitmap doInBackground(String... imagePath) {
// get image path and decode to bitmap
String diveImagePath = imagePath[0];
//String diveImagePath = c.getString(c.getColumnIndex(diveDataBase.KEY_DIVEPICTURE));
File imagePathFile = new File(diveImagePath);
try {
FileInputStream streamIn = new FileInputStream(imagePathFile);
bitmap = BitmapFactory.decodeStream(streamIn);//retrun null if cant convert
}catch (FileNotFoundException e) {
e.printStackTrace();
//Toast.makeText(context, "No Image Found!! Usimng default", Toast.LENGTH_LONG).show();
}//end try catch
return bitmap;
}//end do in background
@Override
protected void onPostExecute(Bitmap bitmap) {
if(bitmap!=null){
displayImage.setBackground(null);
resizedImage = reSizeImage(bitmap);
displayImage.setImageBitmap(resizedImage);
}else{
//Toast.makeText(context, "No Image Found!! Usimng default", Toast.LENGTH_LONG).show();
displayImage.setBackgroundResource(R.drawable.logdive3);
}
}//end onPOstExecute
}//end getBitmap asynch
This inner class is called in the bindView method of CursorAdpter class:
//get bitmap image from base 64 string, and set to image view using asynch class thread
new getBitmapImage().execute(diveImagePath);
put the text on screen first. Maybe default drawables to show things are loading.
Loading 20 bitmaps from file is going to a take a few seconds at least. You should load these asynchronously (from the thread populating your listview). Only your UI thread can set the imageview, but you can load the bitmaps in a worker thread, which is most the work.
You should also be loading the bitmaps using sampling so you aren't loading them at a higher resolution than you can display, otherwise you're reading more data than you need to.
See android tutorial on loading large bitmaps efficiently.