Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

When a simple cursor adapter tries to build a view,it checks if a viewbinder is available.If so it calls the setViewValue first irrespective of the type of view being rendered.Based on the value returned from this function,setViewText or setViewImage is called.In ur case,the first mapping is from SQLiteAdapter.KEY_NAME => R.id.list_item_name,which i suppose is a textView.So when it tries to render,a view binder is available and so it gets called with a textView.But in your viewbinder,u neither check the type of view passed nor the coloumn index.U simply cast it to imageView.So only ClassCastException comes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top