3

Skip to the bottom if you just want to see the question without context

The android app I'm building has a simple table with three columns:

_id INTEGER PRIMARY KEY..., name TEXT, color INT

This table is called categories. I load my categories from the database and feed them into a SimpleCursorAdapter for use with a Spinner like so:

String[] from = new String[] {
        ListDbAdapter.KEY_CATEGORY_NAME,
        ListDbAdapter.KEY_CATEGORY_COLOR };
int[] to = new int[] { R.id.categorySpinnerItem };

mCategorySpinnerAdapter = new SimpleCursorAdapter(this,
    R.layout.category_spinner_item, categoryCursor, from, to);

mCategorySpinnerAdapter
    .setViewBinder(new CategorySpinnerViewBinder());
mCategorySpinner.setAdapter(mCategorySpinnerAdapter);

I set a custom ViewBinder because I want the category name to be the text of the spinner item, and the color to be the background color. My ViewBinder looks like this:

private static final int NAME_COLUMN = 1;
private static final int COLOR_COLUMN = 2;

@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {

    TextView textView = (TextView) view;

    String name = cursor.getString(NAME_COLUMN);
    int color = cursor.getInt(COLOR_COLUMN);

    textView.setText(name);
    textView.setBackgroundColor(color);

    return true;
}

Here is my question (finally)

In the setViewValue method, what is columnIndex supposed to be doing? The documentation says "the column at which the data can be found in the cursor" but when I debug through setViewValue I hit it three times and columnIndex is always 1.

I was expecting the debugger to get into setViewValue once for each entry in the from array, with a columnIndex first of 1 and then 2. Or maybe once for each column in the query results.

The above code works, I can get the desired functionality but only because of my NAME_COLUMN and COLOR_COLUMN constants. I'd be really interested to hear an explanation of setViewValue and its parameters from someone more experienced with custom ViewBinders.

user
  • 86,916
  • 18
  • 197
  • 190
binary lobster
  • 444
  • 1
  • 6
  • 14

2 Answers2

1

I think that you are confused because you missed the part with the return value - true if you bind the data, false otherwise (and the adapter attempts to handle the binding on its own). I think that the idea is like with the OnTouchEvent- giving you the option to consume it or not. So, you are always returning true at index 1, and you are never offered the index 2, because you have binded the view already, that's the only explanation I can think of about having always only 1 in the columnIndex param.

However, I almost haven't used cursor adapters - I find them not-OO, it is way better to create a POJO somewhere else, initializing it however you want using the db columns, and when you have a shaped list of objects just sending them to a "normal" adapter, it sounds more MVC. For example, if at some point you decide that KEY_CATEGORY_NAME will be in the format "cat_name##cat_description" (for example) you have to change the Adapter. Sounds more reasonable to change your class Category, so the getName() will return just "cat_name", and the adapter is the same.

So, because I almost haven't used CursorAdapters , if I am right about the columnIndex, please DO tell me about it, because I am curious but I don't want to create a CursorAdapter and check it myself :)

apps
  • 942
  • 5
  • 8
  • Dang I really wanted this to be the answer! But, in my tests, regardless of whether I return true or false I always come back into the method with 1 as the columnIndex. I even went so far as to return false EVERY time but the results didn't change. My view text and color were still set and columnIndex never changed! – binary lobster Nov 16 '10 at 03:59
  • Hey, check out the source for SimpleCursorAdapter, it's all there, I will write it as a new answer, so I can markup the code, and it would be longer than a comment probably – apps Nov 16 '10 at 04:12
1

In the source of SimpleCursorAdapter, the setViewValue is called in bindView :

bound = binder.setViewValue(v, cursor, from[i]);

where the third param from[i], which is the interesting one, is an iteration over an int[], which represents the column indexes used. However the index for [i] for the iteration itself comes from the int[] to which is passed to the constructor of the adapter, and in your case, it has only 1 item - R.id.categorySpinnerItem

EDIT: In two words, the String[] and the int[] should be equivalent, same size and in same order - for each column name you need an int R.id... first view id will be connected to the first column id with from[0], second with from[1] and so on, and if you pass 10 columns, but you have only 3 R.id-s, you will get only to from[2] :)

apps
  • 942
  • 5
  • 8
  • Excellent idea, I hadn't thought about looking at the source before. And what you point out is very interesting. I suppose then, that the only time columnIndex is useful is when you have multiple views in your to array. Or... is this saying that my to array should have as many values as my from array, even if it would mean the same value multiple times (to = new int[] { R.id.categorySpinnerItem, R.id.categorySpinnerItem})? I will have to play with this a bit more tomorrow. – binary lobster Nov 16 '10 at 04:36
  • now I remember that it WAS that way, which actually is the only way that makes sense, but I have forgotten this :) So you have to change your code, you can't manipulate 1 view with 2 columns (one for name and one for color) – apps Nov 16 '10 at 04:38
  • @binary lobster - it means that column #1 will set the text for textView #1, #2 for #2 and so on – apps Nov 16 '10 at 04:39