RecyclerView not refreshing after rotating the device with an open DialogFragment
Asked Answered
B

1

6

I have a RecyclerView inside a AppCompatActivity. Item insertions and changes are shown and animated correctly after rotating the device.

The problem happens when you:

  1. Tap on an item in the RecyclerView.
  2. A DialogFragment opens prompting if you want to the delete the item.
  3. Rotate the device.
  4. Confirm the deletion in the dialog.
  5. Check the array list. The item has been deleted.
  6. The RecyclerView still shows the item.

Tried using notifyDataSetChanged instead of notifyItemRemoved but didn't work either because the item is still being shown in the RecyclerView.

This is happening with any version of Android.

Simplified code of how the process is being handled:

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

How can I get the RecyclerView to work correctly?


Edit 1:

I have other RecyclerViews in which this works correctly. The only difference is those are inside Fragments instead of AppCompatActivity. I am suspecting that this has something to do with the events onDetachedFromWindow and onAttachedToWindow of the RecyclerView.


Edit 2:

If the dialog is closed (step 4) and opened again it works as expected.


Edit 3:

If the RecyclerView is extracted as a Fragment the problem disappears and works as intended. It is impossible to have the use case described above working correctly in conjunction with AppCompatActivity instead of a Fragment.

Bick answered 23/4, 2016 at 16:55 Comment(12)
When you click on the dialog it should disappear and delete the item in the list. So how come the dialog remains when you change the orientation and it deletes the data?Mcnew
@ReazMurshed The use case is the following: You click an item to delete it. It shows a confirmation dialog with to options ('Cancel' and 'Delete'). You do not click any of the options. You rotate the device. The Dialog is still open. You then choose the option 'Delete' to delete the item. The dialog closes. The item has been deleted from the database. The RecyclerView isn't updated accordingly, it still shows the deleted item.Bick
This is not a proper solution, but anyway, this hack can do the trick you want. You can detect the orientation change easily in onConfigurationChange and can dismiss the dialogue which is showing in the screen. The dialogue showing in the screen is not related to the activity/fragment lifecycle and that's why it remains on the screen.Mcnew
@ReazMurshed But I want the dialog to remain. I don't want the user to have to reopen the dialog again. Besides, I have other RecyclerViews that follow the same use case and are working correctly. This one just baffled me. I'll update my question with some assumption I think might be causing the problem.Bick
@Bick is this your actual code? MyItemDeletionHandler should not compile as written. It's a static inner class, so it should not have access to instance members of MyAppCompatActivity.Fimbriate
@Fimbriate Forgot to call a method of the class. I updated the code. The essence of the code is the same though.Bick
@Bick What is the content of MyDeletionConfirmationDialog?Fimbriate
@Fimbriate Is a DialogFragment that in the method onCreateDialog uses AlertDialog.Builder to create a dialog, and assigns in the setPositiveButton a listener that executes final Message toSend = Message.obtain(callbackHandler.obtainMessage(1, true)); toSend.sendToTarget();. Is that enough or need full code? In Summary is a simple confirmation dialog.Bick
@Bick include the code.Fimbriate
@Bick where is the database delete happening?Fimbriate
just based on the code you've shown, I'm surprised you don't get a NullPointerException when you call sendToTarget() on that Message object.Fimbriate
@Fimbriate The initial version of the post had some database code using LoaderManager. But I reposted the code with a simplified version without database access that still shows the problem. I'll update the description.Bick
C
0

I was facing a similar problem with RecyclerView. When I swiped left to delete an item and then rotate the screen, the item was removed from my dataset but the screen wasn't refreshing like it normaly does when we do the same action without rotating. It seems the adaptar.notifyItemRemoved() wasn't refreshing the screen at all.

I'm using the Nemanja Kovacevic source code as starting point, but I did some changes on it (like adding item click, edit with a dialog, database support, etc).

So I read this post which gave me a hint about what could be going wrong. It seems the adapter.notify was still pointing to the previous adapter referece before rotation. Every time we rotate a new adapter is created at the 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);
  }
}

Maybe it is not ideal (creating static objects is not a good idea), but it solved the problem.

I hope it may help you too.

Cystectomy answered 30/6, 2017 at 22:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.