Как происходит обновление View при вызове requestLayout
Что проверяют
Понимаете ли вы разницу между «пересчитать размеры» и «перерисовать», и что именно происходит от вызова метода до появления кадра на экране. Заодно проверяют, откуда берутся пропущенные кадры в списках.
Как отвечать
requestLayout()выставляет у View флагиPFLAG_FORCE_LAYOUTиPFLAG_INVALIDATEDи поднимается поgetParent()доViewRootImpl, помечая всех родителей по пути. Поэтому вызов у одного элемента стоит как обход целой ветки иерархии, а не одной вьюхи.ViewRootImpl.scheduleTraversals()ставит барьер синхронизации в очередь Looper и регистрирует колбэк вChoreographer. Синхронно ничего не выполняется, работа произойдёт на ближайшем сигнале VSYNC.- В
performTraversals()идут три фазы: measure (onMeasureиsetMeasuredDimension), layout (onLayout, расстановка детей), draw (onDraw). Из-за флага FORCE_LAYOUT кэш измерений не срабатывает, иonMeasureвызывается даже при тех же спецификациях. invalidate()запускает только фазу отрисовки, размеры не пересчитываются. Меняется цвет или текст внутри тех же границ, достаточноinvalidate(). Меняется размер, нуженrequestLayout().- Вызов во время фазы layout приводит к отложенному второму проходу и предупреждению в логе
requestLayout() improperly called by ... during layout.
Где спотыкаются
Считают вызов синхронным и сразу после него читают getWidth() или getHeight(). Актуальные размеры появятся только после следующего прохода, правильный способ это doOnPreDraw или OneShotPreDrawListener.
Путают invalidate() и requestLayout(), вызывая второй ради перекраски и оплачивая полный measure и layout всей иерархии на каждый кадр.
Дёргают requestLayout() из onDraw или в onBindViewHolder на каждый элемент списка. Получается повторный проход в том же кадре и заметный джанк при скролле, лечится setHasFixedSize(true) и отказом от wrap_content у самого списка.
Забывают про глубину: вложенные контейнеры с двойным измерением детей превращают лишний проход в экспоненту по уровням вложенности.