Я хочу исправить мои заголовки в верхней части экрана, как на изображении ниже, без использования внешних библиотек.
В моем случае я не хочу делать это по алфавиту. У меня есть два разных типа представлений (заголовок и нормальный). Хочу закрепить только вверху, последний заголовок.
android
header
android-recyclerview
sticky
Жауме Колом
источник
источник
Ответы:
Здесь я объясню, как это сделать без внешней библиотеки. Это будет очень длинный пост, так что приготовьтесь.
Прежде всего, позвольте мне поблагодарить @ tim.paetz, чей пост вдохновил меня на путешествие по реализации моих собственных липких заголовков с использованием
ItemDecoration
s. Я позаимствовал некоторые части его кода в своей реализации.Как вы, возможно, уже испытали, если вы попытались сделать это самостоятельно, очень трудно найти хорошее объяснение того, КАК на самом деле делать это с помощью этой
ItemDecoration
техники. Я имею в виду, какие шаги? Какая логика за этим? Как сделать так, чтобы заголовок находился в верхней части списка? Незнание ответов на эти вопросы заставляет других использовать внешние библиотеки, а сделать это самостоятельно с помощьюItemDecoration
довольно легко.Первоначальные условия
list
из элементов разного типа (не в смысле «типов Java», а в смысле типов «заголовок / элемент»).list
должен быть заголовком.Здесь я предоставляю полный код для
RecyclerView.ItemDecoration
вызываемогоHeaderItemDecoration
. Затем я подробно объясняю предпринятые шаги.Бизнес-логика
Итак, как мне заставить его приклеиться?
Вы этого не сделаете. Вы не можете сделать
RecyclerView
элемент по своему выбору, просто остановившись и придерживаясь его, если только вы не являетесь гуру пользовательских макетов и не знаете более 12 000 строк кодаRecyclerView
наизусть. Итак, как всегда бывает с дизайном пользовательского интерфейса, если вы не можете что-то сделать, подделайте это. Вы просто рисуете заголовок поверх всего, используяCanvas
. Вы также должны знать, какие элементы пользователь может видеть в данный момент. Просто так получилось, чтоItemDecoration
вы можете получить какCanvas
информацию о видимых элементах, так и информацию. При этом вот основные шаги:В
onDrawOver
методеRecyclerView.ItemDecoration
получения самый первый (верхний) элемент, видимый пользователю.Определите, какой заголовок его представляет.
Нарисуйте соответствующий заголовок поверх RecyclerView с помощью
drawHeader()
метода.Я также хочу реализовать поведение, когда новый предстоящий заголовок встречается с верхним: должно казаться, что предстоящий заголовок мягко выталкивает верхний текущий заголовок из представления и в конечном итоге занимает его место.
Здесь применяется та же техника «рисования поверх всего».
Определите, когда верхний «застрявший» заголовок встречается с новым, предстоящим.
Получите эту точку контакта (это нижняя часть прикрепленного заголовка, который вы нарисовали, и верхняя часть будущего заголовка).
Если элемент в списке нарушает эту «точку контакта», перерисуйте липкий заголовок так, чтобы его нижняя часть находилась наверху элемента, нарушающего границу. Вы добиваетесь этого с помощью
translate()
методаCanvas
. В результате начальная точка верхнего заголовка будет вне видимой области и будет казаться «выталкиваемой предстоящим заголовком». Когда он полностью исчезнет, нарисуйте новый заголовок сверху.Остальное объясняется комментариями и подробными аннотациями в предоставленном мной фрагменте кода.
Использование простое:
Вы
mAdapter
должны реализовать,StickyHeaderInterface
чтобы он работал. Реализация зависит от имеющихся у вас данных.Наконец, здесь я предоставляю гифку с полупрозрачными заголовками, чтобы вы могли понять идею и действительно увидеть, что происходит под капотом.
Вот иллюстрация концепции «рисовать поверх всего». Вы можете видеть, что есть два элемента «header 1» - один, который мы рисуем и остаемся наверху в застрявшем положении, а другой, который берется из набора данных и перемещается вместе со всеми остальными элементами. Пользователь не увидит его внутреннего устройства, потому что у вас не будет полупрозрачных заголовков.
А вот что происходит в фазе «выталкивания»:
Надеюсь, это помогло.
редактировать
Вот моя фактическая реализация
getHeaderPositionForItem()
метода в адаптере RecyclerView:Немного другая реализация в Котлине
источник
private View getHeaderViewForItem(int itemPosition, RecyclerView parent) { int headerPosition = mListener.getHeaderPositionForItem(itemPosition); if(headerPosition != mCurrentHeaderIndex) { mCurrentHeader = mListener.createHeaderView(headerPosition, parent); mCurrentHeaderIndex = headerPosition; } return mCurrentHeader; }
Самый простой способ - просто создать украшение элемента для вашего RecyclerView.
}
XML для вашего заголовка в recycler_section_header.xml:
И, наконец, чтобы добавить украшение элемента в ваш RecyclerView:
С помощью этого украшения элемента вы можете либо сделать заголовок прикрепленным / липким, либо не использовать только логическое значение при создании украшения элемента.
Вы можете найти полный рабочий пример на github: https://github.com/paetztm/recycler_view_headers
источник
wrap_content
), вы также захотите запустить егоfixLayoutSize
после установки текста заголовка.Я сделал свой вариант решения Севастьяна выше
... а вот реализация StickyHeaderInterface (я сделал это прямо в адаптере ресайклера):
Таким образом, в этом случае заголовок - это не просто рисунок на холсте, а представление с помощью селектора или пульсации, прослушивателя кликов и т. Д.
источник
recyclerView.addItemDecoration(HeaderItemDecoration(recyclerView, adapter))
, Извините, не могу найти пример реализации, который использовал. Отредактировал ответ - добавил текст в комментариивсем, кто ищет решение проблемы с мерцанием / миганием, когда у вас уже есть
DividerItemDecoration
. Кажется, я решил это так:похоже, это работает, но может ли кто-нибудь подтвердить, что я больше ничего не сломал?
источник
Вы можете проверить и взять реализацию класса
StickyHeaderHelper
в моем проекте FlexibleAdapter и адаптировать его к вашему варианту использования.Но я предлагаю использовать библиотеку, поскольку она упрощает и реорганизует способ, которым вы обычно реализуете адаптеры для RecyclerView: не изобретайте велосипед.
Я бы также сказал, не используйте декораторы или устаревшие библиотеки, а также не используйте библиотеки, которые делают только 1 или 3 вещи, вам придется самостоятельно объединить реализации других библиотек.
источник
Decorator
s?Другое решение, основанное на слушателе прокрутки. Начальные условия такие же, как в ответе Севастяна
Макет для ViewHolder и липкий заголовок.
item_header.xml
Макет для RecyclerView
Класс для HeaderItem.
Это все толку. Реализация адаптера ViewHolder и прочего нам не интересна.
Интерфейс для просмотра заголовка привязки.
источник
for (int i = position; position >= 0; i--){ //should be i >= 0
Эй,
Вот как вы это делаете, если вам нужен только один тип держателя, когда он начинает выходить из экрана (нас не волнуют никакие разделы). Есть только один способ не нарушить внутреннюю логику утилизации элементов RecyclerView - создать дополнительное представление поверх элемента заголовка recyclerView и передать в него данные. Я позволю коду говорить.
А затем вы просто делаете это в своем адаптере:
Где YOUR_STICKY_VIEW_HOLDER_TYPE - это viewType того, что должно быть липким держателем.
источник
Для тех, кто может беспокоить. Основываясь на ответе Севастьяна, если вы хотите сделать его горизонтальной прокруткой. Просто измените все
getBottom()
наgetRight()
иgetTop()
наgetLeft()
источник
Ответ уже был здесь. Если вы не хотите использовать какую-либо библиотеку, вы можете выполнить следующие действия:
Объяснение:
В
onCreateViewHolder
методе мы можем проверитьviewType
и в зависимости от значения (нашего «особого» вида) создать специальный макет.Например:
где
class ItemElement
иclass TitleElement
может выглядеть обычнымViewHolder
:Так что идея всего этого интересна. Но мне интересно, эффективно ли это, потому что нам нужно отсортировать список данных. И я думаю, это снизит скорость. Если есть мысли по этому поводу, напишите мне :)
А также открытый вопрос: как удержать «особую» раскладку наверху, пока вещи утилизируются. Может быть, совместить все это с
CoordinatorLayout
.источник