Mantenga la posición de desplazamiento al agregar a ListView con desplazamiento inverso sin fin

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

Pregunta

Estoy creando una aplicación de Android similar a un chat, similar a Hangouts.Para este propósito estoy usando un ListView vertical con stackFromBottom=true y transcriptMode="normal".

La lista está ordenada desde mensajes más antiguos (arriba) hasta mensajes más recientes (abajo).En estado normal, ListView se desplaza hacia la parte inferior y muestra el mensaje más reciente.

ListView utiliza un adaptador de desplazamiento infinito inverso para cargar más mensajes (más antiguos) una vez que el usuario se desplaza hasta la parte superior de la lista.Una vez que se cargan los mensajes más antiguos, se agregan al principio de la lista.

El comportamiento deseado es que ListView mantenga su posición de desplazamiento al final de la lista, cuando se agregan mensajes más antiguos al principio.Es decir, cuando se agregan mensajes más antiguos en la parte superior, la vista de desplazamiento debe mostrar los mismos mensajes que se mostraban antes de agregar los mensajes más antiguos.

Por desgracia, esto no funciona.En lugar de mantener la posición de desplazamiento hacia abajo, ListView mantiene la posición de desplazamiento al principio de la lista.Es decir, después de agregar mensajes más antiguos a la lista, la lista muestra los mensajes más importantes (es decir, los más antiguos).

¿Existe una manera fácil de indicarle a ListView que mantenga su posición de desplazamiento hacia abajo, en lugar de hacia arriba, al agregar nuevos elementos en la parte superior?

Actualizar

Para fines de demostración, he minimizado el caso de uso y el código tanto como sea posible.El siguiente ejemplo crea una lista de matriz de cadenas simple.Al hacer clic en un elemento, se agregará otro elemento en la parte superior de la lista.Al hacer clic prolongado en un elemento, se agregará otro elemento al final de la lista.

Todo funciona bien al agregar elementos al final de la lista (clic largo).Al agregar elementos en la parte superior de la lista (simple clic), existen 3 casos:

  • Si la lista se desplazó hasta el final, entonces todo funciona bien.Después de agregar el elemento en la parte superior, la lista aún se desplaza hacia la parte inferior.
  • Si la lista no se desplaza hacia abajo (ni hacia arriba), entonces la lista mantendrá su posición de desplazamiento (desde arriba).Esto hace que parezca que la lista "salta" un elemento hacia arriba.Me gustaría que la lista no "saltara" en este caso, es decir, me gustaría que mantuviera su posición de desplazamiento desde abajo.
  • Si la lista se desplaza hacia la parte superior, sucede lo mismo que en el caso 2.El comportamiento preferido es el mismo que en el caso 2.

(Los últimos 2 casos son de hecho los mismos.Los separé porque el problema se puede demostrar mejor en el caso 2.)

Código

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;
      }
    });
  }
}
¿Fue útil?

Solución

He descubierto esto.Dondequiera que obtenga datos nuevos, llamo a lo siguiente antes de cambiar los datos en el adaptador:

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

Entonces llamo:

adapter.setData(data);

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

Otros consejos

Primero obtenga la primera posición visible (o la última ya que está al revés).Tienes que intentar comprobarlo tú mismo) y luego obtener la parte superior de la Vista que se agrega a la Lista y luego, usando el método setSe;ectionFromTop(), puedes desplazarte hasta la ubicación deseada.

Código:

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

Fuente.

Edite para tener en cuenta información adicional:

Creo que encontré tu problema.Repliqué los casos de uso descritos utilizando una lista independiente como única vista en XML:

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

Solucioné el problema para seguir los casos de uso deseados anidando el diseño en un diseño principal:

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

Después de insertar el registro en su adaptador, puede intentar usar

   yourListView.setSelection(yourAdapter.getCount() - 1);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top