Подтвердить что ты не робот

Как получить смещение в RecyclerView ItemDecorator

Я написал два ItemDecorator для RecyclerView. Каждый добавляет некоторое верхнее смещение в getItemOffsets(). Пусть говорят:

  • Первый декоратор добавляет верхнее смещение 20dp
  • Второй декатор добавляет верхнее смещение 30dp

Теперь, когда я добавляю оба из них в RecyclerView, каждый элемент правильно смещается на 50dp, это хорошо.

Но вот возникает вопрос: Как получить это смещение в onDraw/onDrawOver?

Обычно декораторы рисуют свои вещи, перемещая вещи parent.getChildAt(i) и получая child.getTop(), например, чтобы рисовать над дочерним видом RecyclerView.

Но в этом случае это приведет к смешению рисунка другого декоратора, потому что он также будет использовать child.getTop().

Итак, на данный момент кажется, что оба декоратора должны знать друг о друге и друг о друге.

Я что-то упустил? Надеюсь, я.

РЕДАКТИРОВАТЬ: Я сообщил о проблеме на трекер для Android, и, похоже, это будет сработано. Звезда, чтобы отслеживать прогресс: https://code.google.com/p/android/issues/detail?id=195746

4b9b3361

Ответ 1

tl; dr Нет, вы ничего не пропустили. Но вы можете получить значения, необходимые в getItemOffsets, хотя это кажется мне немного грязным.

В основном существует только один вариант получения украшенной высоты, отличной от управления украшениями: LayoutManager.getDecoratedTop();

OnDraw

В onDraw вы получаете весь холст recyclerView, c.getClipBounds() не содержит никакой информации. Хотя javadoc добавления декораций говорит, что украшения должны просто рисовать с их границами.

Кроме того, получение parent.getLayoutManager().getDecoratedTop() даст вам одинаковые значения в каждом украшении, так как уже слишком поздно здесь эти значения предназначены для макетирования.

Мы слишком поздно, макетирование сделано, и мы его пропустили.

getItemOffsets

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

Я просто почувствовал, что мне нужен этот отказ. Я нашел рабочее решение (смотри mOffset):

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    mOffset = parent.getLayoutManager().getTopDecorationHeight(view);
    outRect.set(0, 10, 0, 0);
}

Это работает, потому что recyclerview при вычислении общей вставки дочернего элемента обновляет локальную переменную LayoutParams пакета представлений. Хотя мы не можем получить доступ к самой переменной параметра layout, вызов getTopDecorationHeight фактически использует (в настоящее время) грязную вставку, что дает нам правильное значение.
Следовательно, вы можете получить смещение предыдущих украшений, прежде чем применять свои собственные!

Чтобы применить его, просто удалите другие декорации при рисовании украшения:

c.drawRect(child.getLeft() + child.getTranslationX(),
        mOffset + layoutManager.getDecoratedTop(child) + child.getTranslationY(),
        child.getRight() + child.getTranslationX(),
        child.getBottom() + child.getTranslationY(), paint);

Это фактически применит другое смещение украшений, а затем нарисует ваше собственное из верного верха.

Некоторые проблемы

Теперь это базовое, работающее решение для стандартного usecase. НО. Если у вас есть разные смещения в зависимости от элемента VIEW_TYPE или позиции адаптера, все становится сложным.

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

Чтобы сделать это, вы можете либо добавить какой-либо пользовательский тег с помощью view.setTag(key, object), либо сделать что-то подобное с объектом RecyclerView.State, который будет передан.