0

My fragment managed by ViewPagerAdapter implements LoaderCallbacks. It queries the database to obtain the Cursor with all the rows that contain favourite field equals 1 (true). When user deselects favourite-toggle_button favourite field in the table updates to be 0 (false). I believe that then Loader call onLoadFinished() to swap cursor so list view should be updated without that specific item inside. But this only happens with the last item in list (most bottom). And when I try to remove not the last item - it removes and reappears immediately. I don't get why it behaves this way.

Any thoughts?

public class SongsFavouriteFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

...

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_container_listview,container,false);

    mAmdmAdapter = new AmdmAdapter(getActivity(), null, 0, R.layout.song_art_item);
    ListView songsListView = (ListView)v.findViewById(R.id.container_listview);
    songsListView.setAdapter(mAmdmAdapter);
    songsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {

            ...
        }
    });
    return v;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    getLoaderManager().initLoader(LOADER_ID, null, this);
    super.onActivityCreated(savedInstanceState);
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    Uri songsUri = AmdmContract.SongEntry.buildSongJoinArtistUri();
    String[] projection = SONG_COLUMNS;
    String selection = TABLE_NAME + "." + COLUMN_FAVORITE + " = 1 "; 
    return new CursorLoader(getActivity(),
            songsUri,
            projection,
            selection,
            null,
            null);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    mAmdmAdapter.swapCursor(cursor);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mAmdmAdapter.swapCursor(null);
    }
}

And adapter

public class AmdmAdapter extends CursorAdapter {    

public AmdmAdapter (Context context, Cursor c, int flags, int layout) {
    super(context, c, flags);
    this.layout = layout;
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
    View view = LayoutInflater.from(context).inflate(layout, parent, false);
    return view;
}

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

    ViewHolder viewHolder = new ViewHolder(view);

...

    if (viewHolder.toggle_song != null) { // here is my favourite toggle button
        viewHolder.toggle_song.setChecked(cursor.getInt(COL_FAVOURITE) == 1);
        viewHolder.toggle_song.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                int favourite = isChecked ? 1 : 0;
                ContentValues value = new ContentValues();
                String where = AmdmContract.SongEntry.TABLE_NAME + "." +
                        AmdmContract.SongEntry._ID + " = " + tableId;
                value.put(AmdmContract.SongEntry.COLUMN_FAVORITE, favourite);
                mContext.getContentResolver()
                        .update(AmdmContract.SongEntry.CONTENT_URI,
                                value, where, null);

                if (songName != null)
                Log.d(DEBUG_TAG, "OnCheckedChanged: id =  " + tableId + ", songName = " + songName +
                        ", favourite =  " + favourite);
            }
            }
        });
    }
}

@Override
public Cursor swapCursor(Cursor newCursor) {
    if (newCursor != null) {
        COl_ID = newCursor.getColumnIndex("_id");
        COL_ARTIST_ID = newCursor.getColumnIndex("artist_id");
        COL_ARTIST_NAME = newCursor.getColumnIndex("artist_name");
        COL_SONG_NAME = newCursor.getColumnIndex("song_name");
        COL_DATE_ADD = newCursor.getColumnIndex("date_add");
        COL_FAVOURITE = newCursor.getColumnIndex("favourite");
        COL_PHOTO = newCursor.getColumnIndex("photo");
    }
    return super.swapCursor(newCursor);
}

    ...
}

UPDATE:

@Override
public int update(
        Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    int rowsUpdated;

    switch (sUriMatcher.match(uri)) {
       ...
        case SONGS: {
            rowsUpdated = db.update(AmdmContract.SongEntry.TABLE_NAME,
                    values, selection, selectionArgs);
            break;
        }
    ...
                default:
                    throw new UnsupportedOperationException("Unknown uri: " + 

uri);
        }
        if (rowsUpdated != 0)
            getContext().getContentResolver().notifyChange(uri, null);

    Log.d(LOG_TAG,"update(): " + rowsUpdated  + " rows updated");
    return rowsUpdated;
}
AlexKost
  • 2,792
  • 4
  • 22
  • 42
  • where are you deleting the entry ? – Blackbelt Jul 04 '15 at 08:47
  • I delete nothing. I only update "favourite" row in database to be "false" when toggle button switches so Loader gets notified, reload cursor, swap it in the adapter and list should appear without this item. But it doesn't work as expected. – AlexKost Jul 04 '15 at 09:36

2 Answers2

0

Assuming that your query to update the database is correct, you are missing a vital piece of the puzzle to put everything together. ContentResolver is not directly linked with the LoaderManager/CursorLoader . getContentResolver().update will not cause the LoaderMananger to restart and to query the database again. What you need it to register a ContentObserver that listens for changes on the Uri you notify after you update the table. When onChange is invoked you have to restart the LoaderManager

Blackbelt
  • 156,034
  • 29
  • 297
  • 305
  • I think it's not the case. I provide update() method code above. In logs I can see that favourite column updates to be 0 and then changes again to be 1 immediately (in emulator I see that item removes and quickly appear again). I don't understand what cause it to do so. – AlexKost Jul 04 '15 at 12:45
0

My mistake is to put setChecked() before setOnCheckedChangeListener().

List reuses items, so when cursor updates the old item fills with new data. setChecked() called for toggle button and it cause to call onCheckedChangeListener registered for already deleted from cursor row.

When I put setOnCheckedChangeListener() before setChecked() everything works as expected. Hope it'll help somebody.

AlexKost
  • 2,792
  • 4
  • 22
  • 42