Как исправить проблемы notifyDataSetChanged / ListView в динамической оболочке адаптера Android

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

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

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

В демонстрации заголовки добавляются динамически путем просмотра на 10 позиций вперед, чтобы найти правильную позицию, в которой элементы списка переключаются на следующую букву. Эта демонстрация показывает важность notifyDataSetChanged (). При нажатии на что-либо приложение вылетает. Это связано с некоторой проверкой работоспособности в ListView ... mItemCount! = Adapter.getItemCount () . Мораль в том, что мы должны уведомить список об изменении данных.

Демо 2 Следующим естественным шагом является уведомление ListView об изменениях при их возникновении. К сожалению, выполнение этого во время прокрутки ListView полностью прерывает все сенсорные взаимодействия, пока приложение не выйдет из сенсорного режима. Чтобы это заметить, вам нужно будет "перекинуть прокрутку" достаточно далеко, чтобы сгенерировать новые заголовки. Нажатие на экран не приведет к остановке прокрутки, и после остановки ни один из элементов списка не станет активным. Это происходит из-за некоторого кода if (! MDataChanged) {/ * очень важного * /} кода в AbsListView.onTouchEvent ().

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

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

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

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

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

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

5
задан Matt Busche 5 April 2013 в 13:16
поделиться