Reputation:
In my app, the user has a list (a recyclerview) of his favorite movies, and for each movie in his favorites he can add a note and in that list I have this recycling problem where when he opens the dialog to add a note he adds the note and the note icon in the recyclerview reflects on that change, it's red in color when a note is added, but then when I click on another item in the list and come back to the item where I just added the note, the note icon goes back to grey and no note is seen in the dialog (the textview where the note should be is empty). The note only shows up when I manage to call my FavoriteFragment's OnResume where a refresh method gets called, it calls CursorAdapter changeCursor
.
Also on click of an item in the list, the view gets expanded to reveal the Add/Edit note action, like so:
@Override
public void onBindViewHolder(final FavoriteHolder holder, final int position) {
// Passing the binding operation to cursor loader
final boolean isExpanded = position == mExpandedPosition;
holder.releasesActions.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
if (isExpanded) {
mPreviousExpandedPosition = position;
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(mPreviousExpandedPosition);
mCurrentPosition = position;
holder.initReleaseActions((Cursor) mCursorAdapter.getItem(mCurrentPosition));
notifyItemChanged(position);
// fixes recycling issue
// refresh only last position
// holder.initReleaseActions();
// mCursorAdapter.getItem(mPreviousExpandedPosition);
}
});
mCursorAdapter.getCursor().moveToPosition(position);
mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor());
}
The initReleaseActions
method is the one which should update the note actions, to reflect changes on the note icon.
Here's its implementation in the FavoriteHolder
:
public void initReleaseActions(Cursor cursor) {
// Setting our components
// User saved release actions
mHeartIcon.setImageResource(R.drawable.ic_favorite_red);
if (cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COL_NOTE_ADDED)) == 1) {
mNoteIcon.setImageResource(R.drawable.ic_note_red);
mNoteText.setText("Edit note");
}
}
My question is why won't this method to its job? I don't understand, in my mind it should work, do I always have to refresh the entire cursor if changes happen?
Oh and here's my implementation of CursorAdapater:
public FavoriteReleasesAdapter(Context context, String sortType, TextView nothingFoundTxt) {
mContext = context;
mDatabaseHelper = DatabaseHelper.getDatabaseHelper(mContext);
mCursor = getUserCursor(sortType);
mInflater = LayoutInflater.from(mContext);
mNothingFoundTxt = nothingFoundTxt;
mAlarmReceiver = new AlarmReceiver();
mIsCoversHidden = SharedPrefManager.read(SharedPrefManager.KEY_PREF_HIDE_COVERS, false);
mCursorAdapter = new CursorAdapter(mContext, mCursor, 0) {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.favorite_release_item, parent, false);
FavoriteHolder holder = new FavoriteHolder(view);
holder.setFavoriteReleaseAdapter(FavoriteReleasesAdapter.this);
holder.setIsCoversHidden(mIsCoversHidden);
holder.setDatabaseHelper(mDatabaseHelper);
holder.setAlarmReceiver(mAlarmReceiver);
holder.setContext(mContext);
view.setTag(holder);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
FavoriteHolder holder = (FavoriteHolder) view.getTag();
holder.setFavoriteDetails(cursor);
}
};
}
// this is the update method that gets called on FavoriteFragment onResume
public void updateCursor(String sortType) {
mCursor = getUserCursor(sortType);
mCursorAdapter.changeCursor(mCursor);
notifyDataSetChanged();
toggleEmptyTxt();
// notifyItemRemoved(mCursor.getPosition());
}
Upvotes: 0
Views: 158
Reputation: 18687
Do I always have to refresh the entire cursor if changes happen?
Yes.
Cursor is like a snapshot of the database when the query was executed. If you change anything on the database, you must update your cursor as well (run a new query and replace the old cursor by a new one). If you don't want to do that, you can convert your cursor to an ArrayList<YourModelClass>
and this way, you can dinamically update specific positions.
Another point about your code:
if (cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COL_NOTE_ADDED)) == 1) {
mNoteIcon.setImageResource(R.drawable.ic_note_red);
mNoteText.setText("Edit note");
}
You only set the red icon but never set the grey icon. However, RecyclerView re-uses a view. So, it may try to re-use a view with red-icon in a position where the movies does not have notes (grey icon). So, correct approach would be:
if (cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COL_NOTE_ADDED)) == 1) {
mNoteIcon.setImageResource(R.drawable.ic_note_red);
mNoteText.setText("Edit note");
} else {
// Re-apply default text and default icon
}
Upvotes: 1