Hi I'm having a few problems with view finder and was wondering if anyone could help.
I have an SQLite database which is fine and am storing small thumbnail images in it as blobs. I know the blobs are being saved as I am able to retrieve them in another area of my application.
Now, I'm trying to use ViewBinder to bind an image from the database to my custom list view. ( you can think of it as a contact manager sort of layout where I have a list of names and numbers with a corresponding image.)
I have tried a few different methods including using a simple cursor adapter and from my research it seems that creating my own view binder seemed the way to do it. The code has no errors however at run time I'm getting a ClassCastException.
Below is the code from my main list activity
listViewCards = (ListView) findViewById(android.R.id.list);
Cursor cursor = dbhelper.getAllContacts();
SimpleCursorAdapter myAdapter = new SimpleCursorAdapter(
getApplicationContext(),
R.layout.listview_each_item,
cursor,
new String[] { SQLiteAdapter.KEY_NAME,SQLiteAdapter.KEY_PHONE, SQLiteAdapter.KEY_COMPANYNAME},
new int[] { R.id.list_item_name,R.id.list_item_phone, R.id.list_item_companyname },0);
ImageView image = (ImageView) findViewById(R.id.list_item_image);
MyViewBinder mvb = new MyViewBinder();
mvb.setViewValue(image, cursor, cursor.getColumnIndex(SQLiteAdapter.KEY_IMAGE));
myAdapter.setViewBinder(mvb);
listViewCards.setAdapter(myAdapter);
and for my custom view binder
public class MyViewBinder implements ViewBinder{
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
ImageView image = (ImageView) view;
cursor.moveToFirst();
byte[] byteArray = cursor.getBlob(columnIndex);
if(image !=null){
image.setImageBitmap(BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length));
return false;
}
return true;
}
Now the logcat
08-14 11:01:15.275: E/AndroidRuntime(2669): FATAL EXCEPTION: main
08-14 11:01:15.275: E/AndroidRuntime(2669): java.lang.ClassCastException: android.widget.TextView cannot be cast to android.widget.ImageView
08-14 11:01:15.275: E/AndroidRuntime(2669): at com .example.npacards.MyViewBinder.setViewValue(MyViewBinder.java:14)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:146)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.widget.CursorAdapter.getView(CursorAdapter.java:250)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.widget.AbsListView.obtainView(AbsListView.java:2457)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.widget.ListView.measureHeightOfChildren(ListView.java:1250)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.widget.ListView.onMeasure(ListView.java:1162)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.view.View.measure(View.java:15473)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.widget.RelativeLayout.measureChild(RelativeLayout.java:602)
08-14 11:01:15.275: E/AndroidRuntime(2669): at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:415)
Line 14 in my view binder is
ImageView image = (ImageView) view;
Could anyone help me understand why this produces a classCastExeption as the view that is being passed into my view binder is
ImageView image = (ImageView) findViewById(R.id.list_item_image);
Any ideas are much appreciated.
A ViewBinder
will get called for every view declared in the to
(adapter's constructor) array passed when creating the adapter. Right now you're not passing the ImageView
(through it's id) so the ViewBinder
will not get called for the ImageView
, it will only get called for the views with the ids R.id.list_item_name
, R.id.list_item_phone
and R.id.list_item_companyname
. This is why you get that ClassCastException
, because you wrongly assume that the view passed to the ViewBinder
is the ImageView
(this is another mistake you made, because you don't look at the ids of the views passed to the ViewBinder
to see which view is set to be binded at that moment with data from the Cursor
).
To solve it you need to let the adapter know that you also want the ImageView
from the row to get binded(notice the extra column and id):
SimpleCursorAdapter myAdapter = new SimpleCursorAdapter(
getApplicationContext(),
R.layout.listview_each_item,
cursor,
new String[] { SQLiteAdapter.KEY_NAME,SQLiteAdapter.KEY_PHONE, SQLiteAdapter.KEY_COMPANYNAME, SQLiteAdapter.KEY_IMAGE},
new int[] { R.id.list_item_name,R.id.list_item_phone, R.id.list_item_companyname, R.id.list_item_image},0);
Then change the ViewBinder
to handle setting the image, letting the adapter bind the other views on its own:
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (view.getId() == R.id.list_item_image) { // we have our ImageView so bind the image
byte[] byteArray = cursor.getBlob(columnIndex);
((ImageView)view).setImageBitmap(BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length));
return true; // return true to let the adapter know that we handled this view on our own
}
return false; // return false for any other view so the adapter will bind the data on its own
}
This:
mvb.setViewValue(image, cursor, cursor.getColumnIndex(SQLiteAdapter.KEY_IMAGE));
is incorrect. You don't call setViewValue()
on your own, the adapter will call it in the getView()
method for each of the views with the ids from the to
array to set the data on them.