2
\$\begingroup\$

I've run into a problem twice just recently. Though I'm getting around it I can't help but feel that it's a rather unconventional method and that there's a better one.

This app is an exercise in using different types of persistent storage. I have 1 table to store short notes. In a ListView those notes are displayed along with the date they were created and a button to delete that note.

Somehow inside an anonymous class' method override declaration for View.OnClickListener.onClick I am left unable to get an item's _id. But I DO have the view and so I place an invisible text view into the layout and every time I bind that view I setText to the _id of the row that is associated with that item.

When the button is clicked the item will be deleted using the _id from the invisible view. YAY it works, however, it seems a little weird to be doing it that way.

Source Code

app/src/main/java/se/frand/app/onetableapp/DateListAdapter.java

@Override
public void bindView(final View view, final Context context, final Cursor cursor) {

    ...

    viewHolder.deleteButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String where = MyContract.NoteEntry.COLUMN_NAME_ID + "=?";

            // Retrieve the id of the row to delete from an invisible textview in the item layout
            // view is from bindView
            String[] args = new String[] {
                    ""+((TextView)view.findViewById(R.id.note_id)).getText() };

            context.getContentResolver().delete(
                    MyContract.NoteEntry.CONTENT_URI, where, args);

            swapCursor(MainActivity.getLogTimes(context));
        }
    });
    viewHolder.noteView.setText(cursor.getString(MainActivity.COL_DATETIME_NOTE));
    // setText of invisible textView to the id from the cursor
    viewHolder.invisibleView.setText(cursor.getString(MainActivity.COL_DATETIME_ID));
}

app/src/main/res/layout/list_item_selected_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    ...

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/item_delete_button"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/note_id"
        android:visibility="invisible"/>

</LinearLayout>

My question is, how would you retrieve the _id?

Here's how I did it in the master branch originally, but it didn't work. I caught the bug when I was working on my first refactor.

String where = MyContract.DateEntry.COLUMN_NAME_ID + "=?";
String[] args = {""+cursor.getInt(MainActivity.COL_DATETIME_ID)};
db.delete(MyContract.DateEntry.TABLE_NAME, where, args);

The problem here is it always gets the first row's id and deletes that row. It appears cursor is starting at the front even though cursor.getString(MainActivity.COL_DATETIME_NOTE) on the other side of the anonymous method is adding the correct data (not all items repeat the first row).

Below is an additional occurrence of me resolving the same problem in the same way.


I had hit this problem just two days prior doing a similar app.

In this app users can view a set of nutritional ingredient items for a given meal. Long clicking a ingredient item brings up an AlertDialogwith optional replacements. Clicking one should replace the ingredient_id in a reference table with the _id of the ingredient selected.

The same problem occurred here. When I selected a dialog choice the ingredient of the first item in the list would be replaced, not the ingredient that was clicked.

Below is the code where I display the AlertDialog and handle the click of a dialog choice.

Source Code

app/src/main/java/se/frand/app/dietplan/IngredientsActivity.java

builder.setTitle(R.string.pick_replace)
    .setItems(ingredients, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int position) {

            // Once again here is where I pull the _id from the view
            String mealitemid = ""+((TextView) view.findViewById(R.id.meal_item_id)).getText();

            SQLiteDatabase db = openOrCreateDatabase(MealsDbHelper.DATABASE_NAME,0,null);
            ContentValues values = new ContentValues();
            values.put(MealItemsContract.MealItemEntry.COL_NAME_INGREDIENT_ID, ids[position]);
            int rows = db.update(
                        MealItemsContract.MealItemEntry.TABLE_NAME,
                        values,
                        MealItemsContract.MealItemEntry.COL_NAME_ID+"=?",
                        new String[] {mealitemid});

            cursor = db.rawQuery(query, new String[]{meal_id});
            adapter.swapCursor(cursor);

            db.close();
        }
});
builder.show();

I have a theory now as to why, before clicking and notifying the listener the cursor is entirely iterated through. Thus when clicked the cursor is in the position of beyond final, which it looks like in this behavior would be the same as the beginning.

So what do you say? How should I be getting these _id values into an inner anonymous listener method declaration? I'd really appreciate any general code review, tips unrelated to this problem in particular.

\$\endgroup\$
1
\$\begingroup\$

Retrieve id outside listener

I think your problem is that you were trying to use the cursor inside the listener, but when the button was pressed, the cursor had already moved to a different row. What you could do is retrieve the id outside the listener, like this:

@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
    ...
    final int id = cursor.getInt(MainActivity.COL_DATETIME_ID);
    viewHolder.deleteButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String where = MyContract.NoteEntry.COLUMN_NAME_ID + "=?";
            String[] args = new String[] {Integer.toString(id)};

            context.getContentResolver().delete(
                    MyContract.NoteEntry.CONTENT_URI, where, args);

            swapCursor(MainActivity.getLogTimes(context));
        }
    });
    viewHolder.noteView.setText(cursor.getString(MainActivity.COL_DATETIME_NOTE));
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.