Какие основные колбеки жизненного цикла кастомной View
Что проверяют
Понимаете ли вы, в каком порядке система измеряет, раскладывает и рисует View и что можно делать в каждом колбеке. Следом почти всегда спрашивают, чем invalidate() отличается от requestLayout() и как поддержать wrap_content.
Как отвечать
Назовите колбеки в порядке вызова:
- конструкторы: для инфлейта из XML нужен вариант с
AttributeSet. Атрибуты читаются черезobtainStyledAttributes()с обязательнымrecycle()или черезwithStyledAttributesиз core-ktx. Здесь же создаютсяPaintиPath; onFinishInflate(): вызывается у View из XML, когда инфлейт закончен, у ViewGroup к этому моменту уже есть дети;onAttachedToWindow(): View попала в окно. Место для подписок, запуска анимаций, регистрации слушателей;onMeasure(): родитель передаёт ограничения вMeasureSpecс режимомEXACTLY,AT_MOSTилиUNSPECIFIED. Результат обязательно отдаётся черезsetMeasuredDimension(), считать его удобно черезresolveSize();onSizeChanged(): размер стал известен или изменился. Здесь пересчитывают геометрию, зависящую от ширины и высоты;onLayout(): расстановка детей, по-настоящему нужен только в ViewGroup;onDraw(): рисование наCanvas, у ViewGroup детей рисуетdispatchDraw();onDetachedFromWindow(): отписки, остановка анимаций, освобождение ресурсов.
Отдельно упомяните сохранение состояния через onSaveInstanceState() и onRestoreInstanceState(): работает, только если у View задан id. И разницу двух запросов: invalidate() только перерисовывает, requestLayout() запускает заново измерение и раскладку.
Где спотыкаются
Создают Paint, Rect или Path прямо в onDraw(). Метод вызывается на каждый кадр, мусор копится, начинаются паузы сборщика, и Lint об этом предупреждает.
Игнорируют режим MeasureSpec и берут только размер. Тогда wrap_content ведёт себя как match_parent, а забытый setMeasuredDimension() роняет приложение с IllegalStateException.
Считают размеры в конструкторе, где width и height ещё равны нулю, или не отписываются в onDetachedFromWindow(), и View утекает вместе с Activity.