ItemDecoration переопределяет getItemOffsets() и анимацию
Я применяю равные поля для моего RecyclerView
с помощью GridLayoutManager
переопределив getItemOffsets()
метод (см. мой код ниже).
Однако при удалении объекта из адаптера анимация удаления вызывается без смещений. Таким образом, anmiation начинается в другом положении, чем объект, который будет удален.
Я пытался получить позицию через getSpanIndex(position)
но позиция (parent.getChildAdapterPosition(view)
) возвращает NO_POSITION
поскольку объект уже был удален из адаптера, когда getItemOffsets()
называется.
Есть ли способ получить смещения в моем случае?
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
GridLayoutManager mgr = parent.getLayoutManager();
int position = parent.getChildAdapterPosition(view);
if (position == RecyclerView.NO_POSITION) {
// here I need to access the position of the current element
// and call outRect.set(left, top , right, bottom);
// which is not possible because it is no longer in the adapter
return;
}
int spanCount = mgr.getSpanCount();
int spanSize = mgr.getSpanSizeLookup().getSpanSize(position);
int spanIndex = mgr.getSpanSizeLookup().getSpanIndex(position, spanCount);
if (spanIndex == spanCount-1) {
// last element
left = space / 2;
right = space;
} else if (spanIndex == 0) {
// first element
left = space;
right = space / 2;
} else {
// middle element
left = space / 2;
right = space / 2;
}
outRect.set(left, top, right, bottom);
}
4 ответа
Попробуйте использовать виды LayoutParams
, Если вы не используете некоторые пользовательские LayoutManager
, он должен содержать информацию, которая вам нужна.
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition();
На самом деле, это не очень хорошее решение получить дочернюю позицию от родителя. Фактическое состояние просмотра может отличаться от реального состояния, потому что в то же время работает либо ItemDecorator, либо LayoutManager, либо RecyclerView для управления ими, либо Adapter для изменения положения некоторых элементов.
Лучше, когда попросить Recycler дать вам ViewHolder для определенного вида. Затем вы можете получить всю необходимую информацию (getLayoutPosition(), getAdapterPosition()).
parent.getChildViewHolder(view).getLayoutPosition();
parent.getChildViewHolder(view).getAdapterPosition;
И еще один совет, не из вашего случая. Если вам нужно получить ItemCount, не используйте adapter.getItemCount(), лучше получить состояние из состояния, потому что количество элементов может быть разным в Adapter и Recycler.
state.getItemCount()
Я наткнулся на эту же проблему здесь, и я прочитал ответы и обсуждения. Это может быть очень поздний ответ, но я отвечу ради тех, кто столкнулся с этой проблемой.
Ответ Джулиана Ос должен был быть помечен как правильный ответ, но на самом деле мало что объяснил.
Вот подробное объяснение:
на ItemDecoration
"s getItemOffsets()
сначала получить позицию предмета parent.getChildViewHolder(view).getAdapterPosition();
Как отметил Ильягорбунов, это более безопасный способ получить точку зрения. Этот метод возврата RecyclerView.NO_POSITION
когда запрашиваемое представление больше не существует. Так что, если это так, иди проверить parent.getChildViewHolder(view).getOldPosition()
которая возвращает старую позицию анимации. Этот метод также возвращает RecyclerView.NO_POSITION
когда ChildViewHolder больше не присутствует в представлении или завершил анимацию.
Если в вашем адаптере есть несколько типов представлений, просто напрямую получите тип представления из parent.getChildViewHolder(view).getItemViewType()
Для полноты вот небольшой фрагмент кода:
override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) {
super.getItemOffsets(outRect, view, parent, state)
var position = parent?.getChildViewHolder(view)?.adapterPosition
if (position == RecyclerView.NO_POSITION) {
val oldPosition = parent?.getChildViewHolder(view)?.oldPosition
if (oldPosition == RecyclerView.NO_POSITION) return
position = oldPosition
}
val viewType = parent?.getChildViewHolder(view)?.itemViewType
when (viewType) {
//do your outRect here
}
}
Итак, после попытки выяснить это в течение некоторого времени, я в конце концов наткнулся на ViewHolder#getOldPosition
,
Javadoc говорит следующее:
Когда LayoutManager поддерживает анимацию, RecyclerView отслеживает 3 позиции для ViewHolders для выполнения анимации.
Если ViewHolder был размечен в предыдущем вызове onLayout, старая позиция сохранит индекс адаптера в предыдущем макете.
Другими словами, getOldPosition() возвращает позицию адаптера ViewHolder, в которой он находился до удаления.
Таким образом, чтобы получить это значение из вашего метода ItemDecoration # getItemOffsets, просто укажите:
parent.getChildViewHolder(view).getOldPosition()
Я проверял это сам, и это действительно работает:).
Вы должны получить spanIndex от
(view.layoutParams as?GridLayoutManager.LayoutParams)?.spanIndex
и то же самое для spanSize
(view.layoutParams as? GridLayoutManager.LayoutParams)?.spanSize
благодаря этому вы в безопасности во время анимации, когда адаптер может иметь новый набор данных и SpanSizeLookup
будет работать с другим набором элементов, чем те, для которых вы рассчитываете смещения
использование адаптера в декораторе предметов - не лучшая идея. старайтесь всегда делать все вычисления только на основе состояния просмотра и ресайклера. Вы можете рассмотреть возможность передачи некоторых данных через теги в представлении или ресурсы в состоянии ресайклера.