RecyclerView не обновляется после поворота устройства с открытым DialogFragment

У меня есть RecyclerView внутри AppCompatActivity. Вставки и изменения элементов отображаются и анимируются правильно после поворота устройства.

Проблема возникает, когда вы:

  1. Нажмите на элемент в RecyclerView.
  2. Откроется DialogFragment с предложением удалить элемент.
  3. Поверните устройство.
  4. Подтвердите удаление в диалоговом окне.
  5. Проверьте список массивов. Товар был удален.
  6. RecyclerView по-прежнему показывает элемент.

Пробовал использовать notifyDataSetChanged вместо notifyItemRemoved, но это тоже не сработало, потому что элемент все еще отображается в RecyclerView.

Это происходит с любой версией Android.

Упрощенный код обработки процесса:

public class MyAppCompatActivity extends AppCompatActivity {
        int positionOfDeletedItem;
        MyObjectRecyclerViewAdapter adapter;
        ArrayList<MyObject> someTestData;
        MyItemDeletionHandler deletionHandlerRemover;

        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.my_activity_layout);

            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
            positionOfDeletedItem = 1;
            deletionHandlerRemover = new MyItemDeletionHandler(this);

            someTestData = new ArrayList<MyObject>(3);
            someTestData.add(new MyObject("A"));
            someTestData.add(new MyObject("B"));
            someTestData.add(new MyObject("C"));

            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));

            adapter = new MyObjectRecyclerViewAdapter(new MyAdapterOnClickEvent.OnItemClick() {
                @Override
                public void onClick(int posicion, int idViaje, View view) {
                    String tag = "Some tag value";
                    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                    Fragment prev = getSupportFragmentManager().findFragmentByTag(tag);
                    if(prev != null)
                        ft.remove(prev);
                    ft.addToBackStack(null);
                    DialogFragment newFragment = MyDeletionConfirmationDialog.newInstance(deletionHandlerRemover);
                    newFragment.show(ft, tag);
                }
            }, someTestData);
            recyclerView.setAdapter(adapter);
        }

        private final static class MyItemDeletionHandler extends Handler {
            private final WeakReference<MyAppCompatActivity> theActivity;

            private MyItemDeletionHandler(MyAppCompatActivity act) {
                theActivity = new WeakReference<MyAppCompatActivity>(act);
            }
            @Override
            public void handleMessage(Message msg) {
                MyAppCompatActivity activity = theActivity.get();
                if(activity != null) {
                    if(msg.what == 1) {
                        activity.deleteTheItem();
                    }
                }
            }
        }

        public void deleteTheItem() {
            someTestData.remove(positionOfDeletedItem);
            adapter.notifyItemRemoved(positionOfDeletedItem);
        }
}





public class MyDeletionConfirmationDialog extends DialogFragment {
    private Message handlerMessage;

    public static MyDeletionConfirmationDialog newInstance(Handler callbackHandler) {
        MyDeletionConfirmationDialog myDialog = new MyDeletionConfirmationDialog();

        Bundle args = new Bundle();
        args.putParcelable("handlerMessage", callbackHandler.obtainMessage(1, true));
        myDialog.setArguments(args);

        return myDialog;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handlerMessage = getArguments().getParcelable("handlerMessage");
    }

    @Override
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());

        alertDialogBuilder.setMessage("Some message");
        alertDialogBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                final Message toSend = Message.obtain(handlerMessage);
                toSend.sendToTarget();
            }
        });
        alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        Dialog dialog = alertDialogBuilder.create();
        dialog.setCanceledOnTouchOutside(true);
        return dialog;
    }
}

Как заставить RecyclerView работать правильно?


Редактировать 1:

У меня есть другие RecyclerView, в которых это работает правильно. Единственная разница в том, что они находятся внутри Fragments вместо AppCompatActivity. Я подозреваю, что это как-то связано с событиями onDetachedFromWindow и onAttachedToWindow из RecyclerView.


Редактировать 2:

Если диалоговое окно закрыто (шаг 4) и снова открыто, оно работает как положено.


Редактировать 3:

Если RecyclerView извлекается как Fragment, проблема исчезает и работает как положено. Невозможно, чтобы описанный выше вариант использования работал правильно в сочетании с AppCompatActivity вместо Fragment.


person OneEyeQuestion    schedule 23.04.2016    source источник
comment
При нажатии на диалог он должен исчезнуть и удалить элемент в списке. Так почему диалоговое окно остается, когда вы меняете ориентацию и удаляет данные?   -  person Reaz Murshed    schedule 24.04.2016
comment
@ReazMurshed Вариант использования следующий: вы щелкаете элемент, чтобы удалить его. Он показывает диалоговое окно подтверждения с опциями («Отмена» и «Удалить»). Вы не выбираете ни один из вариантов. Вы вращаете устройство. Диалог все еще открыт. Затем вы выбираете опцию «Удалить», чтобы удалить элемент. Диалог закрывается. Товар был удален из базы данных. RecyclerView не обновляется соответствующим образом, он по-прежнему показывает удаленный элемент.   -  person OneEyeQuestion    schedule 24.04.2016
comment
Это не правильное решение, но в любом случае этот хак может помочь вам. Вы можете легко обнаружить изменение ориентации в onConfigurationChange и закрыть диалог, отображаемый на экране. Диалог, отображаемый на экране, не связан с жизненным циклом активности/фрагмента, поэтому он остается на экране.   -  person Reaz Murshed    schedule 24.04.2016
comment
@ReazMurshed Но я хочу, чтобы диалог остался. Я не хочу, чтобы пользователю приходилось снова открывать диалоговое окно. Кроме того, у меня есть другие RecyclerViews, которые следуют тому же варианту использования и работают правильно. Этот просто сбил меня с толку. Я обновлю свой вопрос с некоторым предположением, которое, по моему мнению, может быть причиной проблемы.   -  person OneEyeQuestion    schedule 24.04.2016
comment
@OneEyeQuestion это ваш настоящий код? MyItemDeletionHandler не должен компилироваться как написано. Это статический внутренний класс, поэтому он не должен иметь доступа к членам экземпляра MyAppCompatActivity.   -  person Karakuri    schedule 07.06.2016
comment
@Karakuri Забыл вызвать метод класса. Я обновил код. Хотя суть кода та же.   -  person OneEyeQuestion    schedule 07.06.2016
comment
@OneEyeQuestion Каково содержание MyDeletionConfirmationDialog?   -  person Karakuri    schedule 07.06.2016
comment
@Karakuri — это DialogFragment, который в методе onCreateDialog использует AlertDialog.Builder для создания диалога и назначает в setPositiveButton прослушиватель, который выполняет final Message toSend = Message.obtain(callbackHandler.obtainMessage(1, true)); toSend.sendToTarget();. Этого достаточно или нужен полный код? В сводке есть простое диалоговое окно подтверждения.   -  person OneEyeQuestion    schedule 08.06.2016
comment
@OneEyeQuestion включает код.   -  person Karakuri    schedule 08.06.2016
comment
@OneEyeQuestion, где происходит удаление базы данных?   -  person Karakuri    schedule 08.06.2016
comment
только на основе кода, который вы показали, я удивлен, что вы не получаете NullPointerException, когда вы вызываете sendToTarget() для этого объекта сообщения.   -  person Karakuri    schedule 08.06.2016
comment
@Karakuri В первоначальной версии сообщения был некоторый код базы данных, использующий LoaderManager. Но я перепостил код с упрощенной версией без доступа к базе данных, которая все еще показывает проблему. Я обновлю описание.   -  person OneEyeQuestion    schedule 09.06.2016


Ответы (1)


Я столкнулся с аналогичной проблемой с RecyclerView. Когда я провел пальцем влево, чтобы удалить элемент, а затем повернуть экран, элемент был удален из моего набора данных, но экран не обновлялся, как обычно, когда мы выполняем то же действие без поворота. Кажется, что adapter.notifyItemRemoved() вообще не обновлял экран.

Я использую Nemanja Kovacevic в качестве отправной точки, но я внес в него некоторые изменения (например, добавление щелчка элемента, редактирование с помощью диалогового окна, поддержка базы данных и т. д.).

Итак, я прочитал этот сообщение, которое дало мне подсказку о том, что может пойти не так. Кажется, что adapter.notify все еще указывал на предыдущую ссылку на адаптер до поворота. Каждый раз, когда мы поворачиваем, новый адаптер создается в Activity: OnCreate.

public class MainActivity extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener,
    AddAlertDialog.OnAlertSavedListener,
    AlertListAdapter.OnItemDeletedListener {

  static ListAdapter mListAdapter;
  RecyclerView mRecyclerView;
  ... 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    mRecyclerView = (RecyclerView) findViewById(R.id.mainListView);
    mDB = new DatabaseTable(this);

    // Reading all alerts
    ArrayList<Alert> alerts = mDB.getAllAlerts();

    if (mListAdapter == null)
        mListAdapter = new ListAdapter(this, alerts);
  }
}

Может быть, это и не идеально (создавать статические объекты — плохая идея), но проблему решило.

Я надеюсь, что это может помочь и вам.

person João Carlos Silva dos Santos    schedule 30.06.2017