Pull to refresh

Comments 11

Вопрос следующий: почему вы не воспользовались просто дополнительным полем во ViewHolder для position. можно туда записывать позицию последней вьюхи? — это полезно для обработки кликов.
Для обработки выделения элементов достаточно завести удобную сущность
SparseArray<Boolean> seletedArray = new SparseArray<>();
.
выделение/снятие выделения записать достаточно просто будет
seletedArray.put(position, isSelected);

получить аналогично легко
isSelected = seletedArray.get(position,false);

если нужно очистить выделение, то просто вызовем
seletedArray.clear();
adapter.notifyDataSetChanged();

Мне кажется кода меньше будет, нет?
Это ради интереса. Я хочу узнать другую сторону :)
Вопрос следующий: почему вы не воспользовались просто дополнительным полем во ViewHolder для position. можно туда записывать позицию последней вьюхи? — это полезно для обработки кликов.

Потому, что у ViewHolder'а уже есть метод для получения позиции. Единственная проблема, полагаться полностью на эту позицию нельзя, потому что прямо сейчас, когда вы выполняете тот или иной код, ViewHolder уже может находиться в состоянии Scrapped или Dirty (см. документацию к RecyclerView) и в этом случае могут возникнуть проблемы с отображением.

По поводу остальной части комментария. Да, кода меньше, но подход на мой взгляд неверный. Обработка выделения элементов это ведь не только сохранить сам факт выделения — это как минимум:
1. Правильно инициализировать ViewHolder'ы для обработки нажатий.
2. Добавить логику когда какое нажатие что делает.
3. Правильно визуально отобразить выделение.

Метод notifyDataSetChanged() должен вызываться если у нас изменились данные, а в случае выделения, данные остались те же самые, изменилось только их визуальное представление. Опять же, взгляните на официальную документацию:
If you are writing an adapter it will always be more efficient to use the more specific change events if you can. Rely on notifyDataSetChanged() as a last resort.
Просто добавлю для информации:

А я пользовался вот этим методом для получения позиции клика: stackoverflow.com/questions/28296708/get-clicked-item-and-its-position-in-recyclerview
По сути моё решение ничем не отличается, я просто вынес всю логику выделения в отдельный класс, а адаптеру оставил ответственность по визуальному отображению.
Мой совет — при работе с RecyclerView постарайтесь как можно меньше полагаться на результат метода getAdapterPosition(), как я уже упомянул в комментарии выше, нельзя точно сказать в каком состоянии находится ViewHolder во время вызова, так что вместо ожидаемой позиции вы легко можете получить NO_POSITION в ответ.

PS: считается хорошим тоном заворачивать ссылки в тэг
<a href=""></>
Так используйте getLayoutPosition ()!
developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#getLayoutPosition()
В доке же написано что он выдает позицию всегда, даже если уже идет обновление!

Тэги использовать не могу — карма слита вхлам.
Ага, вот только метод вернёт вам (как понятно из названия) позицию во вьюхе, которая, в определённых условиях может отличаться от его позиции в адаптере. Собственно это и есть самая большая сложность в работе с RecyclerView — никогда не знаешь что она сделает с твоими ViewHolder'ми в следующий момент. У меня, например, бывали ситуации когда для одной и той же позиции при инициализации по-очереди зачем-то создавались и байндились два разных ViewHolder'а. Поверьте, я пробовал всё, что вы предлагаете. В итоге остановился на том, что на 100% можно доверять только позиции, полученной из метода onBindVIewHolder(ViewHolder, int).
Спорить не буду — опыта явно меньше чем у вас.
Cсылка на такой холдер для каждого элемента сохраняется в корневом layout'е, используя метод setTag(int, Object) (с моей точки зрения тот ещё костыль).

Есть еще View#setTag(Object), для случаев, когда с View нужно проассоциировать только один тэг.

Всё вроде здорово, но при отображении больших списков таких вот ViewHolder'ов создаётся достаточно много, что плохо влияет на размер используемой памяти.

Почему? Наследники AdapterView, т.е. ListView или GridView, создают ровно столько элементов, сколько помещается на экране, при скроллинге ранее созданные элементы списков/таблиц переиспользуются. То есть помещается у меня в ListView 8 элементов на экране, значит и всего будет создано 8 элементов, пусть даже в адаптере этого ListView находится две-три сотни объектов. На каждый из них будет прицеплен свой собственный ViewHolder, то есть их тоже будет 8 штук. Не такие уж и большие затраты по памяти.

Упомяну еще twoway-view — библиотека, которая решает некоторые из рассмотренных вами проблем с RecyclerView (отсутствие выделения элементов, OnItemClickListeners и прочего).
при скроллинге ранее созданные элементы списков/таблиц переиспользуются

Да, но ранее созданные элементы для данной позиции адаптера, а не вообще ранее созданные. Здесь как раз и кроется ключевое различие между List/GridView и RecyclerView, — он умеет использовать одну и ту же вьюху+холдер для других позиций, тогда как List/GridView ассоциирует конкретную вьюху с конкретной позицией в адаптере. То-есть когда вы скроллите список вниз у вас не используются те самые, первые 8 вьюх, а для каждой новой позиции создаётся своя. А вот когда начинаете скроллить в обратную сторону — тут они могут использоваться заново, если ещё не удалены.

twoway-view действительно классная библиотека, подглядывал там некоторые решения, добавлю ссылку в статью.
Специально создал сейчас простенький проект с ListActivity. Каждый элемент — TextView, в адаптере лежат сто чисел, от 0 до 99. Переопределил метод ArrayAdapter#getView(int, View, ViewGroup) следующим образом:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.e(getClass().getName(), "position: " + position + "; " + ((convertView == null) ? "new one" : "reused"));
    return super.getView(position, convertView, parent);
}


Итог: для позиций 0-11 вывелось «new one», для всех остальных «reused». Проскроллил обратно вверх — для всех позиций вывелось «reused». По-моему это подтверждает мою точку зрения о том, что AdapterView с наследниками точно так же используют ранее созданные View для новых позиций.
Вы только что пошатнули одну из ключевых причин для меня по переходу на новый виджет. Тем более странно, что я был уверен в обратном, поскольку в одном своем древнем проекте наблюдал прямо противоположную картину. Как только доберусь до компьютера обязательно обновлю статью. Спасибо.
Sign up to leave a comment.

Articles