38

I'm brand new to Android development... coming from iPhone and .Net background. I've seen very similar questions to this one, but none of them dealt with the SimpleCursorAdapter.

I have a basic ListActivity which uses a Cursor to bind data from a SQLite query to my ListView:

ListAdapter adapter = new SimpleCursorAdapter(
        this, 
        android.R.layout.simple_list_item_1,  
        c,        
        new String[] {"name"},   
        new int[] {android.R.id.text1}); 

setListAdapter(adapter);

Then when an item is clicked:

public void onListItemClick(ListView l, View v, int position, long id) {

    super.onListItemClick(l, v, position,  id);

    //Difference between this:
    Cursor c = (cursor)l.getItemAtPosition(position);
    //and this??
    Cursor c = (Cursor)l.getAdapter().getItem(position);

    int categoryId = c.getInt(0);
}

Is this the proper way to get the id of the element that was selected? It seems strange, because I wouldn't think I could use my cursor after the database is closed (which is is after I bind). What is the point of the id passed in, when I can't seem to find a way of getting the actual item from that id? Also, I don't understand why the getItemAtPosition() would return a cursor... the cursor is bound to the entire list; not just one row. Finally, if this is correct, is there a difference between the two ways shown of getting the cursor? Thanks.

GendoIkari
  • 11,734
  • 6
  • 62
  • 104

2 Answers2

84

So a couple of points: after you fetch the cursor, you want to call startManagingCursor. This ties the cursor's lifecycle with Activity's lifecycle (so when the Activity gets destroyed the cursor gets closed/cleaned up).

startManagingCursor(c);
ListAdapter adapter = new SimpleCursorAdapter(
        this, 
        android.R.layout.simple_list_item_1,  
        c,        
        new String[] {"name"},   
        new int[] {android.R.id.text1}); 
setListAdapter(adapter);

Also, the database isn't closed, the Cursor typically keeps a live connection to the DB (so the ListView can scroll and do things of that nature that may require future access to the Cursor's contents.

To your core question, the easiest way to do it in onListItemClick is this:

Cursor c = ((SimpleCursorAdapter)l.getAdapter()).getCursor();
c.moveToPosition(position);

You can then use the c.getLong(0) to get the id (assuming you fetched the id column as the first column which is generally the case). However, note that the id is passed in as part of the signature (see the last argument in public void onListItemClick(ListView l, View v, int position, long id)) so you really don't need to fetch it again (but you certainly can if you want to burn the cycles). For accessing other columns you can do the same thing, just change the column index.

starball
  • 20,030
  • 7
  • 43
  • 238
Femi
  • 64,273
  • 8
  • 118
  • 148
  • Thanks! I was calling startManagingCursor, but I didn't understand why until now! A couple questions... So even when I call SQLiteDatabase.close(), that doesn't stop my cursor from accessing it? And ListView doesn't seem to have a getCursor() method. Finally, the id that is passed to my method IS the id from my database automatically? Thanks! – GendoIkari May 27 '11 at 19:51
  • 1
    Ah, if you call `close` then your ListView will probably stop functioning: generally what I do is I reuse my Application object as my database adapter: call open in `onCreate` and close in `onTerminate` and then use that to tie the database connection lifecycle to the app's lifecycle. As far as the `getCursor`, my error: it should be `Cursor c = ((SimpleCursorAdapter)l.getAdapter()).getCursor();`. And yes, the id is what comes out of the database: its quite convenient. – Femi May 27 '11 at 20:12
  • 9
    startManagingCursor is deprecated in APi11 you might want to update your answer to mention using CursorLoader – Noah Jun 07 '11 at 20:56
  • 1
    startManagingCursor is only deprecated if you're targeting one of the .0001% of Android users who have HoneyComb or ICS or want to deal with versioned import madness. – Bjorn Jan 03 '12 at 06:12
  • @Femi How can I, within `onListItemClick`, toggle the visibility of a certain child of the layout I passed into my `CursorAdapter`. I tried `TextView mTextView = (TextView) v.findViewById(R.id.my_text); mTextView.setVisibility(mTextView.isShown()? View.GONE: View.VISIBLE); mCursorAdapter.notifyDataSetChanged();` but it seems to choosing which list items to toggle at random, regardless of the one I click. – ababzy Mar 03 '12 at 12:34
  • Sorry, just noticed the comment: couple of improvements. 1) `onTerminate` in the `Application` object is actually never called, so you should use a separate database manager object that you create in `onCreate` and close in `onDestroy`. 2) @ababzy, because views are recycled you can't use the current visibility status as a flag: if you want to adjust it you need to adjust it based on some other value (probably taken from the current cursor). – Femi May 07 '12 at 14:38
  • 2
    @BjornTipling You can in fact use CursorLoaders with Compatibility package, what's wrong in updating such a wonderful answer. – Gaurav Agarwal May 27 '12 at 19:12
  • Just wanted to add that in the source code of CursorAdapter getItem(position) does exactly this, so there was nothing wrong with GendoIkari's approach. – Pin Feb 19 '13 at 13:26
  • Bjorn, `startManagingCursor` is deprecated - period. That's a fact which is unlikely to change. – ban-geoengineering Oct 27 '14 at 14:17
2

Another way:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View arg1, int position, long arg3) {

            Cursor cursor = (Cursor) parent.getAdapter().getItem(position);
            //TODO
            }
});
Yuliia Ashomok
  • 8,336
  • 2
  • 60
  • 69