web-dev-qa-db-fra.com

Bonne solution pour conserver les éléments de la liste lorsque l'utilisateur fait pivoter le téléphone et conserver toutes les données dans ArrayAdapter

J'utilise Fragment avec listView. Je remplis ArrayAdapter associé à cette liste, par les données reçues dans Loader personnalisé (depuis Internet). ArrayAdapter personnalisé prend en charge le défilement infini (pagination).

Quelle est la meilleure façon de stocker des éléments dans ArrayAdapter lorsque l'utilisateur fait pivoter le périphérique et conserve la position de défilement dans ListView?

Je pense à la création d'un fragment non visuel avec ArrayAdapter et à l'utilisation de la méthode setRetainInstance pour enregistrer les valeurs.

Des suggestions pour une meilleure solution?

41
barbarian

Pour travailler avec le Android Android et cycle de vie du Fragment, vous devez implémenter la méthode onSaveInstanceState dans votre Fragment. Pour simplifier, j'ai supposé que vous disposez d'un tableau de valeurs String que vous pouvez get to (J'étends généralement ArrayAdapter pour encapsuler la construction de la vue et pour fournir une méthode pratique pour accéder à l'ensemble de données sous-jacent):

public void onSaveInstanceState(Bundle savedState) {

    super.onSaveInstanceState(savedState);

    // Note: getValues() is a method in your ArrayAdapter subclass
    String[] values = mAdapter.getValues(); 
    savedState.putStringArray("myKey", values);

}

Vous pouvez ensuite récupérer les données dans votre méthode onCreate (ou onCreateView ou onActivityCreated - voir le Fragment JavaDoc ) comme ceci:

public void onCreate (Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        String[] values = savedInstanceState.getStringArray("myKey");
        if (values != null) {
           mAdapter = new MyAdapter(values);
        }
    }

    ...

}

Cela garantit que tous les événements du cycle de vie seront traités correctement, sans perte de données, y compris la rotation de l'appareil et le passage de l'utilisateur à d'autres applications. Le danger de ne pas utiliser onSaveInstanceState et d'utiliser la mémoire est le danger de Android récupérer cette mémoire. L'état enregistré ne serait pas affecté par cela mais l'utilisation de variables d'instance ou de fragments cachés entraînerait perte de données.

Si savedStateInstance est nul, il n'y a pas d'état à restaurer.

La if (values != null) sert simplement à se prémunir contre la possibilité qu'aucun tableau n'ait été enregistré, mais si vous codez votre ArrayAdapter pour gérer un ensemble de données nul, vous n'en aurez pas besoin.

La solution ultime, si vos lignes sont des instances d'une de vos propres classes et non des éléments de données uniques, est d'implémenter l'interface Parcelable sur cette classe, alors vous pouvez utiliser savedState.putParcelableArray("myKey", myArray). Vous seriez surpris de voir à quel point il est utile de savoir comment implémenter Parcelable - cela vous permet de faire passer vos classes à l'intérieur des intentions et vous permet d'écrire du code beaucoup plus propre.

56
Phil

Lorsque l'appareil est tourné, l'application est redémarrée. Donc onSaveInstance est appelé avant que l'application ne soit détruite. Vous pouvez enregistrer l'adaptateur de baie dans onSaveInstance et lorsque onCreate est enfin appelé au redémarrage de l'application, vous pouvez récupérer l'adaptateur de baie et le définir dans la vue de liste.

6
Vikram Gupta

Il est assez facile d'enregistrer les éléments de votre adaptateur dans onSaveInstance.

Vous devez maintenant enregistrer l'emplacement (défilement). Tu peux le faire

le plus simple

Étant donné que la modification de screenOrientation gâchera les choses de toute façon, vous pouvez autoriser une erreur marginale et simplement, dans votre onSaveInstance, enregistrer le premier élément visible à l'aide de listView.getFirstVisiblePosition().

Ensuite, dans votre onRestoreInstance, vous pouvez récupérer cet index pour faire défiler jusqu'au même élément à l'aide de listview.setSelection(position). Bien sûr, vous pouvez utiliser listview.smoothScrollToPosition(position) mais ce serait bizarre.


à la dure

Pour avoir une position plus exacte, vous devrez descendre d'un niveau supplémentaire: obtenir la position de défilement (pas l'index) et enregistrer cette position. Ensuite, après la restauration, revenez à cette position. Dans votre onSaveInstance, enregistrez la position renvoyée par listview.getScrollY(). Lorsque vous récupérez cette position dans votre onRestoreInstance, vous pouvez revenir à cette position en utilisant listview.scrollTo(0, position).


Pourquoi est-ce vraiment difficile?

Simple. Vous ne pourrez très probablement pas revenir à cette position car votre affichage de liste n'aura pas encore réussi la mesure et la disposition. Vous pouvez surmonter cela en attendant la mise en page après avoir configuré l'adaptateur à l'aide de getViewObserver().addOnGlobalLayoutListener. Dans ce rappel, publiez un exécutable pour faire défiler jusqu'à la position.


Je conseille d'utiliser la première "méthode simple" car en général, lorsque l'orientation de l'écran a été modifiée, l'utilisateur recule probablement ses doigts. Nous avons juste besoin de lui montrer les mêmes éléments "donner ou prendre".

5
Sherif elKhatib

lorsque l'orientation change le cycle de vie de l'activité appelle onPause puis onRestart Faites quelque chose comme ça -

@Override
protected void onRestart() {
    super.onRestart();
    mAdapter.getFilter().filter(getFilterSettings());
    mAdapter.notifyDataSetChanged();
}
1
mjosh