43

I'm trying to bind data from my SQLiteDatabase to a ListView. I'm currently using a SimpleCursorAdapter to fill in my ListView. Unfortunately this doesn't seem to work with setting a CheckBox's checked attribute.

This is how I do it now; instead of changing the CheckBox's checked status the adapter is filling in the value to the text argument, so the value is displayed right of the CheckBox as text.

Java:

setListAdapter( new SimpleCursorAdapter( this,
      R.layout.mylist,
      data,
      new String[] { Datenbank.DB_STATE, Datenbank.DB_NAME },
      new int[] { R.id.list_checkbox, R.id.list_text }
    ) );

mylist.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/LinearLayout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>

<CheckBox android:text=""
    android:id="@+id/list_checkbox"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="false"
    ></CheckBox>

<TextView android:text=""
    android:id="@+id/list_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    ></TextView>

</LinearLayout>

Edit: The field in the database is of course of type boolean and I've also tried to assign an id to the checked field to fill the value in.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
svens
  • 11,438
  • 6
  • 36
  • 55

3 Answers3

37

You could set a custom SimpleCursorAdapter.ViewBinder:

SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(/* ur stuff */);
cursorAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
    public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
        if(columnIndex == 1) {
            CheckBox cb = (CheckBox) view;
            cb.setChecked(cursor.getInt(1) > 0);
            return true;
        }
        return false;
    }
});

The setViewValue method is invoked for every column you specify in the SimpleCursorAdapter constructor and gives you a good place to manipulate some (or all) of the views.

Josef Pfleger
  • 74,165
  • 16
  • 97
  • 99
  • 3
    Only problem is that this is not available in api level 3 (Android 1.5). This is available from api level 5. – Samik R Aug 14 '10 at 21:38
  • 1
    This technique worked very well for me to get images loaded into a list view. Thanks. – Ian Leslie Dec 28 '10 at 14:14
  • 4
    The docs say available since API Level 1. Also, it should be pretty safe to target level 5 and up now: http://developer.android.com/resources/dashboard/platform-versions.html – plainjimbo Apr 08 '11 at 21:51
33

I'm not sure how you would do this aside from creating a custom Adapter that overrode newView/bindView or getView, depending on what you override (ResourceCursorAdapter is a good one).

Ok, so here's an example. I didn't test to see if it would compile because I'm at work, but this should definitely point you in the right direction:

public class MyActivity extends ListActivity {

    MyAdapter mListAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Cursor myCur = null;

        myCur = do_stuff_here_to_obtain_a_cursor_of_query_results();

        mListAdapter = new MyAdapter(MyActivity.this, myCur);
        setListAdapter(mListAdapter);
    }


    private class MyAdapter extends ResourceCursorAdapter {

        public MyAdapter(Context context, Cursor cur) {
            super(context, R.layout.mylist, cur);
        }

        @Override
        public View newView(Context context, Cursor cur, ViewGroup parent) {
            LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            return li.inflate(R.layout.mylist, parent, false);
        }

        @Override
        public void bindView(View view, Context context, Cursor cur) {
            TextView tvListText = (TextView)view.findViewById(R.id.list_text);
            CheckBox cbListCheck = (CheckBox)view.findViewById(R.id.list_checkbox);

            tvListText.setText(cur.getString(cur.getColumnIndex(Datenbank.DB_NAME)));
            cbListCheck.setChecked((cur.getInt(cur.getColumnIndex(Datenbank.DB_STATE))==0? false:true))));
        }
    }
}
MattC
  • 12,285
  • 10
  • 54
  • 78
  • Could you give me an example, because I'm really new into android programming and haven't seen something like this yet? – svens Oct 01 '09 at 21:00
  • Sure thing, lemme dig something up and I'll post it. – MattC Oct 01 '09 at 21:09
  • Thanks !! It worked. There seems to be no getBoolean() function ;-).. I'm now using cbListCheck.setChecked( (cur.getInt(cur.getColumnIndex(Datenbank.DB_STATE))==0? false:true) ); which does the trick. – svens Oct 01 '09 at 21:36
  • An optimization would be moving the do_stuff_here_to_obtain_a_cursor_of_query_results(); out of onCreate and into a background thread so long-running operations don't lock the UI thread. Look into the AsyncTask class to get you started there. Of course if it's going to be fast every time you run that query, it's not necessary. – MattC Oct 01 '09 at 21:39
  • Thanks for the tip, I'll have a look on that. You really saved my life, my todo app looks really cool now. – svens Oct 01 '09 at 21:42
  • I'm also going to update my example to use your code since getBoolean is a syntax error. – MattC Oct 01 '09 at 21:42
  • Could you also shortly explain why you use MyActivity.this as Context? I've only used this and it works too. – svens Oct 01 '09 at 21:50
  • It's just habit. Android loves to use anonymous inner classes when declaring click handlers and other things of that nature. When you need to reference the context in those situations, I generally use MyPublicClassName.this – MattC Oct 02 '09 at 11:27
  • Because once you're in an anonymous inner class, "this" references that inner class, not the public class that also doubles as a reference to the context. Does that make sense? – MattC Oct 02 '09 at 11:30
  • Hi MattC, an extended question: How to bind checked event handler to the checkbox so that when I click the checkbox of a list item, the database also change? Can you please edit the code to do that? – emeraldhieu Jun 28 '11 at 09:05
  • 2
    @Emerald214 Check out http://developer.android.com/reference/android/widget/CompoundButton.html#setOnCheckedChangeListener(android.widget.CompoundButton.OnCheckedChangeListener). In the bindView you'd call cbListClick.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { // put your code here }); – MattC Jun 28 '11 at 20:13
  • 3
    No need to override newView, the superclass does exactly that, it returns an inflated copy of the layout you provided in the constructor. – Mark Renouf Sep 23 '11 at 13:53
  • @Mark true, but I was trying to be verbose so the OP could see where they could override initialization if needed. – MattC Sep 26 '11 at 16:10
0

You can solve that problem by creating a custom CheckBox widget like so:

package com.example.CustomCheckBox;    
public class CustomCheckBox extends CheckBox {
     public CustomCheckBox(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
     }
     public CustomCheckBox(Context context, AttributeSet attrs) {
      super(context, attrs);
     }
     public CustomCheckBox(Context context) {
      super(context);
     }
     protected void onTextChanged(CharSequence text, int start, int before, int after) {
      if (text.toString().compareTo("") != 0) {
       setChecked(text.toString().compareTo("1") == 0 ? true : false);
       setText("");
      }
     }
    }

The onTextChanged function will be called when the ListView binds the data to the CheckBox (ie. Adding either "0" or "1"). This will catch that change and add in your boolean processing. The 1st "if" statement is needed so as to not create infinite recursion.

Then provide your custom class in to the layout file as such:

<com.example.CustomCheckBox
 android:id="@+id/rowCheckBox"
 android:layout_height="fill_parent"
 android:layout_width="wrap_content" />

That should do it!

Nick
  • 1
  • I like this implementation the best, however when I try it, my program crashes when it tries to inflate my CustomCheckBox object. Why would this be? – finiteloop Mar 28 '11 at 02:24