Последний элемент RecyclerView мигает каждый раз, когда элемент удаляется

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

last item blinks

мой код для удаления

public void deleteData(long id){
    Uri uri = ContentUris.withAppendedId(URI, id);
    getContentResolver().delete(uri, null, null);
}

код при свайпе:

deleteData(viewHolder.getItemId());

обратите внимание, что у меня есть другая активность, которая использует тот же код RecyclerAdapter, ContentProvider и макета, даже большинство реализаций и подходов к методам одинаковы, но этот работает отлично без каких-либо миганий, так что это довольно странная ситуация для меня.

Есть ли конкретная причина, которая может вызвать эту проблему? Я уже пытался отключить анимацию, например

recyclerView.getItemAnimator().setChangeDuration(0);

or

((DefaultItemAnimator) recyclerViewObject.getItemAnimator()).setSupportsChangeAnimations(false);

Но ничего из этого не сработало, не говоря уже о том, что я хочу, чтобы анимация работала.

Изменить: Загрузка контрольных точек:

при создании:

"main@4668" prio=5 tid=0x2 nid=NA runnable java.lang.Thread.State: RUNNABLE на com.jackz314.todo.HistoryActivity.onCreateLoader(HistoryActivity.java:803) на android.support.v4.app. LoaderManagerImpl.createLoader(LoaderManager.java:539) в android.support.v4.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:548) в android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:603) в com .jackz314.todo.HistoryActivity.displayAllNotes(HistoryActivity.java:394) в com.jackz314.todo.HistoryActivity.deleteExpiredNotes(HistoryActivity.java:767) в com.jackz314.todo.HistoryActivity.onCreate(HistoryActivity.java:129)< br> в android.app.Activity.performCreate(Activity.java:6975) в android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213) в android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770) в android. app.ActivityThread.handleLaunchActivity(ActivityThr ead.java:2892) в android.app.ActivityThread.-wrap11(ActivityThread.java:-1) в android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
в android.os.Handler. dispatchMessage(Handler.java:105) в android.os.Looper.loop(Looper.java:164) в android.app.ActivityThread.main(ActivityThread.java:6541) в java.lang.reflect.Method.invoke(метод .java:-1) в com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

onDelete (удалить элемент):

"main@4668" prio=5 tid=0x2 nid=NA runnable java.lang.Thread.State: RUNNABLE на com.jackz314.todo.HistoryActivity.deleteData(HistoryActivity.java:791) на com.jackz314.todo.HistoryActivity$4 .onSwiped(HistoryActivity.java:434) в android.support.v7.widget.helper.ItemTouchHelper$4.run(ItemTouchHelper.java:686) в android.os.Handler.handleCallback(Handler.java:789) в android.os .Handler.dispatchMessage(Handler.java:98) в android.os.Looper.loop(Looper.java:164) в android.app.ActivityThread.main(ActivityThread.java:6541) в java.lang.reflect.Method. вызывать (Method.java:-1) в com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

Надеюсь, это поможет

Редактировать: после небольшого расследования я обнаружил, что последний элемент на самом деле не мигал, кажется, что RecyclerView сначала изменил свою длину/размер, отрезал раздел, который имеет такой же размер, как и удаленный элемент (удаление), чтобы последний элемент (в зависимости от размера удаляемого элемента) не был виден некоторое время, а затем RecyclerView удаляет элемент, который должен быть удален в начале. Я все еще работаю над этим, пытаясь понять, потому ли это, что я где-то напутал порядок, или это какая-то другая причина.


comment
У меня была аналогичная проблема, пока я не добавил этот код. Мой дизайн RecyclerAdapter DetailsActivity и ListActivity, который я удаляю в деталях и сообщаю, что список, привязанный к коду адаптера, находится в деталях в разделе btnDelete ListActivity.removeListRow(position); // Строка кода выше вызывает метод в ListActivity, чтобы уведомить переработчик об изменениях   -  person Vector    schedule 18.09.2017
comment
есть ли у вас такой код в действии, который удаляет строку данных // Этот метод вызывается из DetailsActivity и уведомляет представление Recycler о том, что БД была изменена // и метод вносит те же изменения в представление Recycler вроде синхронизации DB и Recycler View public static void removeListRow(int position) { dbList.remove(position); mAdapter.notifyItemRemoved(позиция); mAdapter.notifyItemRangeChanged(position, dbList.size()); }   -  person Vector    schedule 18.09.2017
comment
У меня нет кода, который удаляет строки данных, потому что все автоматически обрабатывается cursorLoader, и поэтому это так меня сбивает с толку. Попозже попробую ваш метод, спасибо.   -  person Jack    schedule 18.09.2017
comment
@ Грендель, можешь подробнее объяснить, как ты добавил ListActivity.removeListRow(position)? Я подумал, что это может решить проблему, но не знаю, как именно это работает.   -  person Jack    schedule 19.09.2017


Ответы (6)


Наконец получил!

Еще одна глупая ошибка, это совсем не сложно, я все думал, почему у меня эта проблема только на этом действии, а не на предыдущем, и вдруг понял, что макет в двух действиях разный, оказывается я установите высоту RecyclerView на wrap_content, так что я полагаю, что произошло то, что макет сказал системе обернуть содержимое в RecyclerView, и когда вы удаляете элемент, его технически больше нет, поэтому, хотя у меня есть анимация для удаленный элемент, элемент немедленно удаляется с уровня макета, поэтому кажется, что Android сначала отрегулировал высоту RecyclerView, а затем удалил элемент, что на самом деле произошло, так как RecyclerView установлено на wrap_content, он удаляет положение представления (элемент) сразу после удаления, а не после анимации, и поэтому это выглядит как мерцание.

В любом случае, решение состоит в том, чтобы просто изменить высоту RecyclerView с wrap_content на любое другое значение, если вы используете ConstraintLayout, как я, просто измените высоту на 0dp, а затем ограничьте RecyclerView чем-то другим или краем.

Спасибо всем, кто пытался помочь мне решить мою проблему, я признаю, что это одна из самых глупых, но разочаровывающих проблем, с которыми я когда-либо сталкивался.

Я полагаю, что есть и другие, которые также столкнулись с той же проблемой, поэтому просто проверьте, установлена ​​ли высота на wrap_content.

person Jack    schedule 23.09.2017
comment
У меня такая же проблема, но это решение не работает. Но ваше объяснение дало мне подсказку. Большое спасибо. - person Muhammed Aydogan; 21.03.2020

Я застрял с тем же, что и вы. Я считаю, что проблема в том, что CursorLoader перезапускается каждый раз, когда мы удаляем что-то из базы данных, поэтому onLoadFinished срабатывает каждый раз и меняет курсоры.

Проблема может быть «решена», если мы загрузим наши данные из курсора в список и передадим их адаптеру. Тем не менее, я считаю это обходным путем, а не реальным решением.

person kjkszpj    schedule 23.09.2017
comment
Я нашел решение, спасибо за помощь, вы проверили свой файл макета и увидели, что высота установлена ​​​​на wrap_content? (Проверьте мой ответ) - person Jack; 23.09.2017

Давайте поместим точку останова в объект данных, который связывается с RecyclerViewAdapter, и проверим, мерцают ли данные или нет. В большинстве случаев, подобных этой ситуации, я понимаю, что данные могут быть изменены, и сделать изменение представления неконтролируемым. Поскольку я не знаю, как вы обрабатываете данные, я не могу дать вам более подробные ответы.

person Tuan Thanh Ho    schedule 18.09.2017
comment
Все обрабатывается cursorLoader, я собирался опубликовать конкретные методы, с помощью которых я обрабатываю данные, но, поскольку операция cursorLoader включает слишком много других классов и методов, я действительно не знаю, что я должен опубликовать здесь, может вы даете мне конкретные инструкции о том, что я должен разместить здесь или где установить точку останова? Спасибо! - person Jack; 18.09.2017
comment
@ jackz314 В вашем RecyclerViewAdapter поместите 1 точку останова в объект данных списка, который вы объявляете в этом классе, и еще 1 точку останова в методе удаленного элемента. Затем следите за данными, которые меняются после удаления элемента. Если ваш объект списка изменен в странном порядке, проверьте метод дескриптора данных после удаления элемента, если нет, проверьте метод дескриптора представления в вашем классе адаптера. - person Tuan Thanh Ho; 18.09.2017
comment
@ jackz314 Вместо того, чтобы публиковать кучу кода, просто добавьте комментарий, в котором рассказывается, что вы разрабатываете. Имена всех классов помогут и из какого класса вы выполняете удаление. Под дизайном я подразумеваю MVP, где у вас есть определенные классы для обработки событий и с RecyclerViewAdapter, поскольку вы знаете, что он должен работать с другими классами. Вы можете включить имена ваших XML-файлов. - person Vector; 18.09.2017

Вы пробовали отключить анимацию?

adapter.setHasStableIds(true);
recyclerView.setItemAnimator(null);
person Yamini Balakrishnan    schedule 18.09.2017

Jackz этот код ниже находится в DetailsActivity, у которого есть btnDelete, он срабатывает и переходит к DBHelper, удаляет запись и возвращается к DetailsActivity, затем вызывает ListActivity, чтобы удалить элемент из списка на основе позиции, а не идентификатора записи.

   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);
        }
    });
}

Вот код, который находится в ListActivity, который общается с адаптером.

    // 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());
}
person Vector    schedule 19.09.2017
comment
У меня это не сработало, взгляните на мое обновление, похоже, что RecyclerView делает что-то в неправильном порядке. - person Jack; 20.09.2017
comment
@ jackz314 Итак, мое предложение: удалить запись, затем удалить элемент из списка, затем уведомить с помощью адаптера, что элемент был удален по положению, и, наконец, с помощью адаптера изменить диапазон элементов, как в моем коде выше. элементы, работая над размером представления списка, устанавливая высоту представления карты, которое заполняет список - person Vector; 22.09.2017
comment
У меня есть решение, на самом деле оно не имеет ничего общего с реальным программным процессом, просто я был глуп и забыл установить высоту RecyclerView на что-то отличное от wrap_content, в любом случае спасибо за помощь! - person Jack; 23.09.2017

У меня точно такая же проблема, и это сводит меня с ума.

Ваше решение не работает в моем случае, возможно, потому, что мой RecyclerViewer находится внутри LinearLayout?

Единственный обходной путь, который я нашел до сих пор, - отключить подобную анимацию.

cardRecyclerView.itemAnimator = null

Но я действительно хочу снова включить анимацию, так как без нее она немного прерывистая.

Вот мой макет, есть ли у кого-нибудь идеи, чтобы последний элемент не мигал при удалении карты? заранее спасибо

<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>
person Benjamin Lefevre    schedule 21.04.2019