Reputation: 5614
I'm using a cursor loader with the RecyclerView
, and everything else works just fine except every time I remove an item from the RecyclerView, the last item blinks, like this
my code for deletion is
public void deleteData(long id){
Uri uri = ContentUris.withAppendedId(URI, id);
getContentResolver().delete(uri, null, null);
}
code on swipe:
deleteData(viewHolder.getItemId());
note that I have another activity that's using the same RecyclerAdapter
, ContentProvider
and layout code, even most of the implementations and method approaches are the same, but that one works perfectly without any blinks, so it is a pretty weird situation for me.
Is there a specific reason that can cause this problem? I already tried disabling the animation like
recyclerView.getItemAnimator().setChangeDuration(0);
or
((DefaultItemAnimator) recyclerViewObject.getItemAnimator()).setSupportsChangeAnimations(false);
But none of that worked, not to mention that I do want the animations to work.
Edit: Break points upload:
onCreate:
"main@4668" prio=5 tid=0x2 nid=NA runnable java.lang.Thread.State: RUNNABLE at com.jackz314.todo.HistoryActivity.onCreateLoader(HistoryActivity.java:803) at android.support.v4.app.LoaderManagerImpl.createLoader(LoaderManager.java:539) at android.support.v4.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:548) at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:603) at com.jackz314.todo.HistoryActivity.displayAllNotes(HistoryActivity.java:394) at com.jackz314.todo.HistoryActivity.deleteExpiredNotes(HistoryActivity.java:767) at com.jackz314.todo.HistoryActivity.onCreate(HistoryActivity.java:129)
at android.app.Activity.performCreate(Activity.java:6975) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) at android.app.ActivityThread.-wrap11(ActivityThread.java:-1) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6541) at java.lang.reflect.Method.invoke(Method.java:-1) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
onDelete(remove item):
"main@4668" prio=5 tid=0x2 nid=NA runnable java.lang.Thread.State: RUNNABLE at com.jackz314.todo.HistoryActivity.deleteData(HistoryActivity.java:791) at com.jackz314.todo.HistoryActivity$4.onSwiped(HistoryActivity.java:434) at android.support.v7.widget.helper.ItemTouchHelper$4.run(ItemTouchHelper.java:686) at android.os.Handler.handleCallback(Handler.java:789) at android.os.Handler.dispatchMessage(Handler.java:98) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6541) at java.lang.reflect.Method.invoke(Method.java:-1) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Hope those helps
Edit: After a little investigation, I found out that the last item didn't actually blink, it seems that the RecyclerView
first changed it's length/size, cuts off a section that has the equal size as the deleted item(deleting), so that the last item(depending on the size of the deleted item) is not visible for a while, and then the RecyclerView
deletes the item that's supposed to be deleted at the beginning. I'm still working on it, trying to figure out whether it's because I messed up the order somewhere or it's some other reason.
Upvotes: 4
Views: 2348
Reputation: 116
Your solution does not work in my case, probably because my RecyclerViewer is inside a LinearLayout?
Exactly! I pulled it out from LinearLayout and customize RV constraints in the same way as LinearLayout was and after that android:layout_height="0dp" did the trick!
Now the animation is perfectly smooth
Upvotes: 0
Reputation: 1
I exactly have the same issue and it makes me crazy.
Your solution does not work in my case, probably because my RecyclerViewer is inside a LinearLayout?
The only workaround I found so far is to disable animation like that
cardRecyclerView.itemAnimator = null
But I really want to re-enable animation since without it is a little bit choppy.
Here is my layout, has someone any idea to avoid the last item blinking when a card is removed? thank you in advance
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="wrap_content">
<fragment
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.bendk97.platenumber.activities.main.Toolbar"/>
<fragment
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.bendk97.platenumber.activities.main.SearchBar"/>
<ProgressBar
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminateTint="@android:color/white"
android:id="@+id/progressBar" android:visibility="invisible"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Upvotes: 0
Reputation: 5614
Finally got it!
Yet another stupid mistake, it's not complicated at all, I kept thinking, why I'm having this problem only on this activity, but not my previous one, and I suddenly realized that the layout in the two activities are different, turns out I set the RecyclerView
's height to wrap_content
, so I suppose what happened is that, the layout told the system to wrap the content in the RecyclerView
, and when you delete an item, it's technically not there anymore, so, even though I have animations for the deleted item, the item is removed immediately from the layout's level, that's why it seems like Android adjusted the RecyclerView
's height first, and then delete the item, what actually happened is, since RecyclerView
is set to wrap_content
, it removes the position of the view(item) immediately after delete, not after the animation, and that's why it looks like a blink.
Anyways, the solution is just simply to change the RecyclerView
's height from wrap_content
to any other value, if you are using ConstraintLayout
like I did, just change the height to 0dp
, then constraint the RecyclerView
to something else, or the edge.
Thanks to everyone who tried to help me solve my problem though, I admit this is one of the stupidest yet frustrating problems I ever had.
I believe there're others who also encountered the same problem, so just go and check if the height is set to wrap_content
.
Upvotes: 7
Reputation: 13
I'm stuck with the same thing as you. I believe the problem is that CursorLoader restarts every time we delete something from the database, so onLoadFinished is triggering every time and swapping cursors.
The problem can be "solved" if we load our data from the cursor into the List and pass that to the adapter. Still, i find this as a workaround, not a real solution.
Upvotes: 1
Reputation: 3235
Jackz this code below is in the DetailsActivity that has a btnDelete it fires and goes over to DBHelper and deletes the record and returns to DetailsActivity then makes a call to ListActivity to remove the item from the list based on position NOT record ID
private void addListenerOnButtonDelete() {
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Calls the Method deleteDBRow in DatabaseHelper
// which acts on the TABLE_INFO to remove a record by getting the record ID
helper.deleteDBRow(String.valueOf(dbList.get(position).getID()));
ListActivity.removeListRow(position);
// Code line above calls Method in ListActivity to notify recycler view of changes
// NOTICE the List keeps items by position not ID <== READ
etWebSite.setText("");
etUN.setText("");
etPW.setText("");
etSecQuestion.setText("");
etSecAnswer.setText("");
etNotes.setText("");
Intent intent = new Intent(DetailsActivity.this, ListActivity.class);
startActivity(intent);
}
});
}
Here is the code that lives in the ListActivity that talks to the Adapter
// This method is called from DetailsActivity and notifies Recycler View that the DB was changed
// and the method makes the same changes to the Recycler View kind of a sync of DB and Recycler View
public static void removeListRow(int position) {
dbList.remove(position);
mAdapter.notifyItemRemoved(position);
mAdapter.notifyItemRangeChanged(position, dbList.size());
}
Upvotes: 0
Reputation: 2411
Have you tried that, to disable animation?
adapter.setHasStableIds(true);
recyclerView.setItemAnimator(null);
Upvotes: 0
Reputation: 1
Let place the break point at the data object, which bind with the RecyclerViewAdapter and check if the data is blinked or not. In the most of case like this situation, I realize the data maybe is effected and make view change uncontrollable. Because I don't know how you handle the data, so I can't give more detail answers for you.
Upvotes: 0