Сохраняйте положение прокрутки при добавлении в ListView с обратной бесконечной прокруткой

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

Вопрос

Я создаю Android-приложение, похожее на чат, похожее на Hangouts.Для этой цели я использую вертикальный ListView с stackFromBottom=true и transcriptMode="normal".

Список отсортирован от старых сообщений (вверху) к более молодым сообщениям (внизу).В обычном состоянии ListView прокручивается до самого низа, показывая самое младшее сообщение.

ListView использует адаптер обратной бесконечной прокрутки для загрузки большего количества (старых) сообщений, как только пользователь переходит к началу списка.Как только старые сообщения загружаются, они добавляются в начало списка.

Желаемое поведение заключается в том, что ListView сохраняет свою позицию прокрутки в нижней части списка, когда более старые сообщения добавляются в его верхнюю часть.То есть, когда старые сообщения добавляются сверху, в режиме прокрутки должны отображаться те же сообщения, которые были показаны до добавления старых сообщений.

К сожалению, это не работает.Вместо сохранения положения прокрутки вниз, ListView сохраняет положение прокрутки в верхней части списка.То есть, после добавления старых сообщений в список, в списке отображаются самые верхние (то есть самые старые) сообщения.

Есть ли простой способ указать ListView сохранять свою позицию прокрутки снизу, а не сверху, при добавлении новых элементов в его верхнюю часть?

Обновить

В демонстрационных целях я максимально свел к минимуму варианты использования и код.В следующем примере создается простой список строковых массивов.Нажатие на элемент добавит еще один элемент в начало списка.Длительное нажатие на элемент добавит еще один элемент в нижней части списка.

Все работает нормально при добавлении элементов в нижней части списка (длинный щелчок).При добавлении элементов в верхней части списка (простой щелчок) возможны 3 варианта:

  • Если список был прокручен до самого низа, то все работает нормально.После добавления элемента в начало список по-прежнему прокручивается до самого низа.
  • Если список не прокручивается ни до низа (ни до верха), то список сохранит свою позицию прокрутки (сверху).Это делает вид, что список "перескакивает" на один элемент вверх.Я бы хотел, чтобы список не "прыгал вверх" в этом случае, т. е. я бы хотел, чтобы он сохранял свое положение прокрутки снизу.
  • Если список прокручивается до самого верха, то происходит то же самое, что и в случае 2.Предпочтительное поведение такое же, как и в случае 2.

(Последние 2 случая на самом деле одинаковы.Я разделил их, потому что проблема может быть лучше продемонстрирована в случае 2.)

Код

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;
      }
    });
  }
}
Это было полезно?

Решение

Я уже разобрался в этом.Где бы вы ни получали новые данные, я вызываю следующее перед изменением данных в адаптере:

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

Тогда я звоню:

adapter.setData(data);

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

Другие советы

Сначала получите первое видимое положение (или последнее с тех пор, как оно перевернуто.Вы должны попробовать и проверить это сами), а затем получить верхнюю часть представления, которое добавляется в список, а затем, используя метод setSe;ectionFromTop(), вы можете прокрутить до нужного места.

Код:

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

Источник.

Отредактируйте, чтобы учесть дополнительную информацию:

Я думаю, что нашел вашу проблему.Я воспроизвел описанные вами варианты использования, используя отдельный список в качестве единственного представления в XML:

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

Я исправил проблему, чтобы следовать вашим желаемым вариантам использования, вложив макет в родительский макет:

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

после того как вы вставите запись в свой адаптер, вы можете попробовать использовать

   yourListView.setSelection(yourAdapter.getCount() - 1);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top