Редактируемый список в Android
RecyclerView с редактируемым содержимымВладельцам телефонов и планшетов
Товарищи, у меня есть просьба: пожалуйста, потестируйте приложение и отпишите мне, какую используете версию Android и насколько хорошо всё работает. Спасибо.
Начало
Всё началось с того, что на работе нужно сделать несколько списков с редактируемым содержимым. ListView не подходит по паре причин: он принимает фокус на свои элементы (не давая пользоваться текстовыми полями) и вообще устарел. Здесь я использовал RecyclerView.
Как работает RecyclerView
Он создаёт столько элементов, сколько умещается на экране, плюс небольшой запас. Как только пользователь начинает прокручивать список, элементы, вышедшие из зоны видимости, убираются с экрана, заполняются другим содержимым и ставятся в то место, где нужны новые. Если в элементах есть поля ввода, изменения, сделанные в них, не сохранятся, а затираются при переиспользовании вьюшек.
Самый очевидный вариант — это навешать TextWatcher'ов. Но вместо того, чтобы сохранять данные непрерывно, мне всё же хотелось бы делать это лениво, при необходимости. Так появляется две ситуации для сохранения: прокрутка (переиспользование элементов) и, собственно, момент, когда данные нужно сохранить.
Реализация
Добавим ViewHolder'у метод refresh, который вытащит содержимое из полей и положит в модели.
@Override
public void refresh() {
mSampleModel.value = mSampleText.getText().toString();
mSampleText.clearFocus(); // чтобы текстовые поля, будучи переиспользованными, появлялись без фокуса
}
Будем звать его, когда вьюха прячется и готовится к переиспользованию. В адаптере:
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
((SampleViewHolder) holder).refresh();
}
Теперь модели изчезающих видов исчезающих вьюшек будут обновляться. А у тех видов, которым после изменения текста посчастливилось выжить, нужно будет запросить данные непосредственно когда потребуется обновление моделей (перед сохранением, например).
LinearLayoutManager man = (LinearLayoutManager) mRecycler.getLayoutManager();
int i = man.findFirstVisibleItemPosition();
int end = man.findLastVisibleItemPosition();
if (i >= 0 && end >= 0) {
while (i <= end) {
((SampleViewHolder) mRecycler.findViewHolderForAdapterPosition(i)).refresh();
i++;
}
}
Всё, хватаем модели и сохраняем.
Тестовое приложение
Я написал приложение для иллюстрации этой задачи. Надеюсь, что оно абсолютно адекватно, то есть:
шустро прокручивает список (у меня, например, разница между 25 и 200 элементами незаметна);
добавляет элементы по нажатию на плюс и удаляет по нажатию на корзину;
показывает актуальные значения текстовых полей по нажатию на кнопку «список»;
сохраняет значения полей при повороте экрана;
не падает ни при каких условиях; я всяко его мучил, но и вы попробуйте
работает на старых версиях Android. Тестировано на Android 2 с чем-то.
Скачать тестовое приложение (разумеется, в настройках нужно разрешить ставить приложения из неизвестных источников)
Changelog
Версия 1.1. Исправлено IndexOutOfBoundsException, изредка возникавшее при попытке удалить несколько элементов списка одновременно, включая последний.