androidlistviewsimplecursoradapter

Android ListView SimpleCursorAdapter TextView and ImageButton


I have a ListView backed by a SQLite db table which displays a song title (TextView) and a button (ImageButton) in the list item XML for each row. I setup an onClick() event for the button to take action on the song on the row they clicked on.

The ListView correctly displays all contents from the db table using a SimpleCursorAdapter.

Here's the question: when I click on the ImageButton it correctly calls the onClick event for the button with parameter (View view); how do I get to the Cursor row data for the row in which the button was clicked from the View passed into the event? I need the row _ID value in order to act on the correct song. I also have access to the dbAdapter in a Class field if I can get there from that object.

Notes: When I add an ImageButton to the Item List the onItemClickListener no longer fires if I click on the row containing the song or on the button.

And please, if there is a better design pattern to give the user the functionality of selecting a ListView item and performing an action on it, let me know. My intention is to eventually add 2-3 buttons per row for Delete, Info, Play, etc.

// Load ListView with previously downloaded files
dbHelper = new DBHelper(this);

// Create Cursor holding db data
Cursor cursor = dbHelper.fetchData();

// Map db columns to view ids
String[] columns = new String[]{
        DBContract.Songs.COLUMN_NAME_NAME,
        DBContract.Songs.COLUMN_NAME_LOADED_DATETIME
};

int[] to = new int[]{
        R.id.songName,
        R.id.songDateLoaded
};

// Create the dbAdapter
dbAdapter = new SimpleCursorAdapter(this, R.layout.songs, cursor, columns, to, 0);

// Assign the adapter to the ListView
ListView listView = findViewById(R.id.songsListView);
listView.setAdapter(dbAdapter);

// Anonymous OnItemClickListener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {...

Solution

  • With Mike's persistent and patient assistance, I implemented the solution below.

    The key is using a setViewBinder() on the SimpleCursorAdapter and then assigning the db row key to the Tag property of the ImageButton. Then, in XML, define an onClick() event and in that event, you now have access to the db row key from the view.getTag() method.

    // Bind the Cursor record _ID to the ImageButton Tag property;
    // So when it is called, we can delete the record with the Tag property value.
    dbAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
        @Override
        public boolean setViewValue(View view, Cursor cursor, int i) {
            if (view.getId() == R.id.songName) {
                final long id = cursor.getLong(cursor.getColumnIndex(DBContract.Songs._ID));
                final ImageButton delete = ((View)view.getParent()).findViewById(R.id.deleteSongButton);
                delete.setTag(id);
            }
            return false;
        }
    });
    
    public void DeleteSong(View view) {
        final long id = (long) view.getTag();
        dbHelper.RemoveSong(id);
        dbHelper.fetchSongsAndUpdateAdapter(dbAdapter);
        Toast.makeText(this, "Song removed!", Toast.LENGTH_LONG).show();
    }
    

    Thanks Mike!