Maintenir la position de défilement lors de l'ajout à la liste d'envoi avec le défilement sans fin inversé

StackOverflow https://stackoverflow.com//questions/22051556

Question

Je construis une application Android semblable à la discussion, semblable à Hangouts. Pour cette fin, j'utilise une liste verticale avec stackFromBottom=true et transcriptMode="normal".

La liste est triée à partir de messages plus anciens (haut) aux messages plus jeunes (en bas). En état normal, la liste de liste est défilée en bas, montrant le message le plus jeune.

La listeView utilise une adaptateur de défilement sans fin inversé pour charger davantage de messages (plus anciens) une fois que l'utilisateur fait défiler vers le haut de la liste. Une fois que les messages plus anciens sont chargés, ils sont ajoutés au sommet de la liste.

Le comportement souhaité est que la vue ListView conserve sa position de défilement au bas de la liste, lorsque des messages plus anciens sont ajoutés au sommet de celui-ci. C'est-à-dire que lorsque les messages plus anciens sont ajoutés au sommet, la vue de défilement doit afficher les mêmes messages affichés avant d'ajouter les messages plus anciens.

Malheureusement, cela ne fonctionne pas. Au lieu de maintenir la position de défilement vers le bas, La liste de liste conserve la position de défilement en haut de la liste . C'est-à-dire qu'après ajout de messages plus anciens à la liste, la liste indique les messages les plus importants (i.e Oilest).

Y a-t-il un moyen facile de dire à la liste de maintenir sa position de défilement au bas, au lieu du haut, lors de l'ajout de nouveaux éléments au sommet de celui-ci?

mise à jour

À des fins de démonstration, j'ai minimisé le cas d'utilisation et le code autant que possible. L'exemple suivant crée une liste de matrices de chaînes simples. En cliquant sur un élément ajoutera un autre élément en haut de la liste. Un clic long sur un élément ajoutera un autre élément au bas de la liste.

Tout fonctionne bien lorsque vous ajoutez des éléments au bas de la liste (clic long). Lors de l'ajout d'éléments en haut de la liste (clic simple), il y a 3 cas:

  • Si la liste a été défilée en bas, tout fonctionne bien. Après avoir ajouté l'élément en haut, la liste est toujours défilée en bas.
  • Si la liste n'est pas défilée en bas (ni au sommet), la liste maintiendra sa position Scroll-y (du haut). Cela rend la liste apparaître à "sauter" un élément vers le haut. J'aimerais que la liste ne «saute» dans ce cas, c'est-à-dire que j'aimerais qu'il maintienne sa position Scroll-y à partir du bas.
  • Si la liste est défilée en haut, il arrive alors que 2. Le comportement préféré est identique au cas 2.

(les 2 derniers cas sont en fait la même chose. Je les ai séparés parce que le problème peut être mieux démontré dans le cas 2.)

code

public class MyListActivity extends Activity {
  int topIndex = 1;

  int bottomIndex = 20;

  protected final void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_list_activity);

    final ListView list = (ListView) findViewById(R.id.list);
    list.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
    list.setStackFromBottom(true);

    final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1);
    for (int i = topIndex; i <= bottomIndex; i++) {
      adapter.add(Integer.toString(i));
    }
    list.setAdapter(adapter);

    list.setOnItemClickListener(new OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        adapter.insert(Integer.toString(--topIndex), 0);
      }
    });

    list.setOnItemLongClickListener(new OnItemLongClickListener() {
      @Override
      public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        adapter.add(Integer.toString(++bottomIndex));
        return true;
      }
    });
  }
}

Était-ce utile?

La solution

Je l'ai compris.Partout où vous obtenez de nouvelles données, j'appelle les éléments suivants avant de modifier les données de l'adaptateur:

int firstVisibleItem = list.getFirstVisiblePosition();
int oldCount = adapter.getCount();
View view = list.getChildAt(0);
int pos = (view == null ? 0 :  view.getBottom());

Puis j'appelle:

adapter.setData(data);

list.setSelectionFromTop(firstVisibleItem + adapter.getCount() - oldCount + 1, pos);

Autres conseils

Obtenez d'abord la première visible (ou la dernière depuis sa position à l'envers. Vous devez essayer de vérifier vous-même), puis obtenez le haut de la vue qui est ajouté à la liste, puis à l'aide de la méthode SETSE; ectionfromtop ()Vous pouvez faire défiler jusqu'à l'emplacement souhaité.

code:

int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0); //or upside down (list.size - 1)
int top = (v == null) ? 0 : v.getTop(); //returns the top of the view its Y co-ordinates. [Doc][1]
mList.setSelectionFromTop(index, top);

Source.

Modifier pour compte pour obtenir des informations supplémentaires:

Je pense que j'ai trouvé votre problème.J'ai répliqué vos cas d'utilisation décrite en utilisant une liste autonome comme seule vue sur XML:

<ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</ListView>

J'ai corrigé le problème pour suivre les cas d'utilisation désirés en nidifiant la mise en page dans une mise en page parent:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</FrameLayout>

Après avoir inséré l'enregistrement dans votre adaptateur, vous pouvez essayer d'utiliser

   yourListView.setSelection(yourAdapter.getCount() - 1);

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top