← Назад к вопросам
Что нужно чтобы вызвался onLayout у View?
2.0 Middle🔥 131 комментариев
#UI и вёрстка
Комментарии (1)
🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизм вызова onLayout() у View
Чтобы понять, что вызывает onLayout(), нужно рассмотреть весь процесс лэйаута в иерархии View. onLayout() — это защищённый метод, который вызывается, когда родительский контейнер определяет положение и размеры своих дочерних View.
Основные условия вызова onLayout():
1. Изменение размеров или иерархии View
- Добавление/удаление дочерних View
- Изменение видимости View (
VISIBLE,INVISIBLE,GONE) - Изменение размеров View (через
LayoutParams) - Изменение контента, влияющее на размеры (текст, изображения)
2. Запрос перерисовки лэйаута
// Эти методы запрашивают пересчет лэйаута
view.requestLayout()
view.invalidate()
view.forceLayout()
// Для всей иерархии
view.post {
view.requestLayout()
}
3. Системные события
- Изменение конфигурации (поворот экрана)
- Изменение размера окна (multi-window mode)
- Изменение системных настроек (размер шрифта)
Как работает процесс:
// Пример структуры вызовов в ViewGroup
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Родитель размещает дочерние элементы
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
// Вычисляем положение дочернего элемента
int childLeft = ...;
int childTop = ...;
int childRight = ...;
int childBottom = ...;
// Вызываем layout() у дочернего элемента
child.layout(childLeft, childTop, childRight, childBottom);
}
}
Детальный механизм:
Шаги, приводящие к вызову onLayout():
- requestLayout() устанавливает флаги
PFLAG_FORCE_LAYOUTу View и всех родителей до корня - ViewRootImpl планирует выполнение
performTraversals()в следующем цикле сообщений - В
performTraversals()вызываетсяperformLayout():private void performLayout() { // Вычисляются размеры и положение host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); } - View.layout() проверяет необходимость перерисовки:
public void layout(int l, int t, int r, int b) { boolean changed = setFrame(l, t, r, b); // Изменились ли границы? if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); // Вот здесь вызывается наш метод! mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; } }
Важные особенности:
Для View (не-Group):
onLayout()— пустая реализация, так как у обычной View нет дочерних элементов- Вызывается, но не делает ничего значимого
Для ViewGroup:
- Должен быть обязательно переопределён
- Определяет положение всех дочерних View
- Вызывает
child.layout()для каждого дочернего элемента
Отличие от onMeasure():
// onMeasure() определяет РАЗМЕРЫ
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// Вычисляем желаемые размеры
setMeasuredDimension(calculatedWidth, calculatedHeight)
}
// onLayout() определяет ПОЛОЖЕНИЕ
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
// Располагаем дочерние элементы
child.layout(childLeft, childTop, childRight, childBottom)
}
Практический пример кастомного ViewGroup:
class CustomLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// 1. Сначала измеряем детей
measureChildren(widthMeasureSpec, heightMeasureSpec)
// 2. Определяем свои размеры
val width = View.getDefaultSize(suggestedMinimumWidth, widthMeasureSpec)
val height = View.getDefaultSize(suggestedMinimumHeight, heightMeasureSpec)
setMeasuredDimension(width, height)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
// Этот метод вызовется автоматически после onMeasure()
// когда система решит, что нужно перерасположить элементы
var currentTop = t
for (i in 0 until childCount) {
val child = getChildAt(i)
if (child.visibility != GONE) {
// Размещаем ребенка вертикально друг под другом
child.layout(
l,
currentTop,
l + child.measuredWidth,
currentTop + child.measuredHeight
)
currentTop += child.measuredHeight
}
}
}
}
Ключевые моменты для запоминания:
- onLayout() вызывается автоматически системой при необходимости
- Не вызывайте
onLayout()напрямую — используйтеrequestLayout() - Метод получает уже вычисленные границы View
- Для ViewGroup обязательно переопределять
onLayout()для размещения детей - Процесс лэйаута всегда следует за процессом измерения (
measure → layout → draw) - Изменение размеров или положения View внутри
onLayout()может привести к бесконечному циклу
Понимание механизма вызова onLayout() критически важно для создания корректно работающих кастомных ViewGroup и оптимизации производительности UI.