5

Ho un RecyclerView all'interno di un AppCompatActivity. Gli inserimenti e le modifiche degli elementi vengono mostrati e animati correttamente dopo aver ruotato il dispositivo.RecyclerView non si aggiorna dopo aver ruotato il dispositivo con un DialogFragment aperto

Il problema si verifica quando si:

  1. Toccare un elemento nella RecyclerView.
  2. A DialogFragment apre il prompt se si desidera eliminare l'elemento.
  3. Ruota il dispositivo.
  4. Confermare la cancellazione nella finestra di dialogo.
  5. Controllare l'elenco di array. L'oggetto è stato cancellato.
  6. Il RecyclerView mostra ancora l'elemento.

provato ad utilizzare notifyDataSetChanged invece di notifyItemRemoved, ma non ha funzionato o perché la voce è ancora mostrato nel RecyclerView.

Questo sta accadendo con qualsiasi versione di Android.

semplificato il codice di come il processo viene gestita:

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

Come posso ottenere il RecyclerView per funzionare correttamente?


Edit 1:

ho altri RecyclerView s in cui questo funziona correttamente. L'unica differenza è che sono all'interno di Fragment s anziché AppCompatActivity. Sospetto che questo abbia qualcosa a che fare con gli eventi onDetachedFromWindow e onAttachedToWindow dello RecyclerView.


Edit 2:

Se la finestra è chiusa (fase 4) e riaperto funziona come previsto.


Edit 3:

Se il RecyclerView viene estratto come un Fragment il problema scompare e funziona come previsto. È impossibile che il caso d'uso descritto sopra funzioni correttamente insieme a AppCompatActivity anziché a Fragment.

+0

Quando si fa clic sulla finestra di dialogo, questo dovrebbe scomparire e cancellare l'elemento nell'elenco. Quindi, come mai il dialogo rimane quando cambi l'orientamento e cancella i dati? –

+0

@ReazMurshed Il caso d'uso è il seguente: Fai clic su un elemento per eliminarlo. Mostra una finestra di conferma con le opzioni ('Annulla' e 'Elimina'). Non fai clic su nessuna delle opzioni. Ruota il dispositivo. La finestra di dialogo è ancora aperta. Quindi scegli l'opzione 'Elimina' per eliminare l'elemento. La finestra di dialogo si chiude. L'oggetto è stato cancellato dal database. Il 'RecyclerView' non viene aggiornato di conseguenza, mostra ancora l'elemento eliminato. – OneEyeQuestion

+0

Questa non è una soluzione adeguata, ma in ogni caso, questo hack può fare il trucco che vuoi. È possibile rilevare facilmente la modifica dell'orientamento in 'onConfigurationChange' e può chiudere il dialogo visualizzato sullo schermo. Il dialogo visualizzato sullo schermo non è correlato al ciclo di vita di attività/frammento ed è per questo che rimane sullo schermo. –

risposta

0

Mi trovavo di fronte a un problema simile con RecyclerView. Quando ho fatto scorrere verso sinistra per eliminare un elemento e quindi ruotare lo schermo, l'elemento è stato rimosso dal mio set di dati ma lo schermo non si è aggiornato come fa normalmente quando eseguiamo la stessa azione senza ruotare. Sembra che l'adaptar.notifyItemRemoved() non abbia affatto aggiornato lo schermo.

Sto usando il codice sorgente Nemanja Kovacevic come punto di partenza, ma ho apportato alcune modifiche su di esso (come l'aggiunta di clic sull'elemento, modifica con una finestra di dialogo, supporto database, ecc.).

Così ho letto questo post che mi ha dato un suggerimento su cosa potrebbe andare storto. Sembra che adapter.notify continuasse a puntare al riferimento dell'adattatore precedente prima della rotazione. Ogni volta che si ruota un nuovo adattatore viene creato alla attività: 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); 
    } 
} 

Forse non è l'ideale (la creazione di oggetti statici non è una buona idea), ma ha risolto il problema.

Spero che possa essere d'aiuto anche a voi.