Как исправить проблемы notifyDataSetChanged / ListView в динамической оболочке адаптера Android - PullRequest
5 голосов
/ 30 декабря 2010

Сводка: Попытка динамически добавить строки заголовка в ListView через специальную оболочку адаптера.У ListView возникают проблемы с синхронизацией позиции прокрутки.Предоставлен демонстрационный проект с возможностью запуска.

Я хотел бы динамически добавлять элементы в список на основе значений в CursorAdapter, на несколько позиций впереди того, что в данный момент просматривает пользователь.Для этого у меня есть адаптер, который оборачивает CursorAdapter и сохраняет индексированное новое содержимое в SparseArray.ListView необходимо обновить, когда элементы добавляются в пользовательский адаптер, но я столкнулся с множеством ловушек, пытаясь заставить его работать, и хотел бы получить несколько советов.

Демонстрационный проект можно скачать здесь: DynamicSectionedList.zip

В демоверсии заголовки добавляются динамически, если посмотреть на 10 мест вперед, чтобы найти правильную позицию, в которой элементы списка переключаются на следующую букву.Каждая реализация notifyDataSetChanged имеет проблемы, как описано:

Демонстрация 1 Эта демонстрация демонстрирует важность notifyDataSetChanged ().При нажатии на что-либо, приложение будет зависать.Это связано с проверкой работоспособности в ListView ... mItemCount != adapter.getItemCount().Морально то, что мы должны уведомить список, что данные изменились.

Демонстрация 2 Естественным следующим шагом является уведомление ListView об изменениях, когда они происходят.К сожалению, выполнение во время прокрутки ListView полностью прерывает взаимодействие сенсорного экрана до тех пор, пока приложение не выйдет из сенсорного режима.Вам нужно будет "пролистать свиток" достаточно далеко, чтобы генерировать новые заголовки, чтобы заметить это.Прикосновение к экрану не приведет к остановке прокрутки, и после остановки ни один из элементов списка не будет активным.Это происходит из-за некоторого if (!mDataChanged) { /* do very important stuff */ } кода в AbsListView.onTouchEvent ().

Демо 3 Чтобы исправить это, в Демо 3 вводится флаг pendingChanges, и пользовательский адаптер получает notifyDataSetChangedIfNeeded (), которыйможет вызываться ListView, как только он вошел в «безопасное» состояние для изменений.Первая точка, где изменения должны быть уведомлены, находится в ListView.layoutChildren (), поэтому я переопределил этот метод, чтобы сначала уведомить об изменениях при необходимости, а затем вызвать через.Пролистав хотя бы один заголовок, затем щелкните элемент списка.

Это не совсем правильно, хотя я не совсем уверен, почему.При нажатии или выборе элемента с помощью клавиатуры / трекбола список обновляется без правильной синхронизации старой позиции.Он прокручивается до верхней части списка, что недопустимо.

Демо 4 Проблема прокрутки в Демо 3 может быть преодолена, по крайней мере, в сенсорном режиме.Добавляя вызов метода notifyDataSetChangedIfNeeded () при касании, происходит изменение данных в такое время, когда все взаимодействия касанием работают должным образом и позиция списка синхронизируется должным образом.

Однако я не могунайти аналог для этого, когда устройство не находится в сенсорном режиме, не говоря уже о том, что оно определенно похоже на взлом.Список почти всегда прокручивается до самого верха, я не могу выяснить, из-за чего он иногда сохраняет правильную позицию.

Поскольку Android борется со мной на каждом этапе пути, я чувствую, что должен быть лучший подход.Пожалуйста, попробуйте демо, если какие-то исправления могут быть применены, чтобы это работало, что было бы здорово!

Большое спасибо всем, кто может разобраться в этом, надеюсь, если мы сможем заставить код работать, это будет полезно для другихпытаясь выполнить ту же оптимизацию для списков с заголовками.

1 Ответ

5 голосов
/ 31 декабря 2010

Основная проблема с подходом, представленным выше, заключалась в том, что набор данных изменялся непосредственно из кода, выполняемого суперклассами.Заполняя заголовки в getView (), я изменял данные в том, что могло быть серединой операции или «небезопасным» состоянием в суперклассах.Вот почему все сенсорные взаимодействия прерываются, когда onNotifyDataSetChanged вызывается во время прокрутки.Данные должны быть добавлены из внешнего источника, а не из методов адаптера.

Это можно сделать с помощью обработчика или AsyncTask, чтобы добавить заголовки к адаптеру и вызвать его notifyDataSetChanged.Они будут выполняться в потоке пользовательского интерфейса и не будут влиять на состояние суперкласса.Спасибо примеру EndlessAdapter от CommonsWare за представление концепции AsyncTask для добавления данных в список.

Хаки onTouchEvent () и layoutChildren () больше не нужны после внесения этого изменения.

...