← Назад к вопросам
В чем разница между процессами измерения у ViewGroup и Layout в Jetpack Compose?
1.7 Middle🔥 81 комментариев
#UI и вёрстка#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между ViewGroup и Layout в Jetpack Compose
Эти два подхода к измерению и расположению элементов представляют два поколения Android UI-разработки. Хотя оба решают одну задачу, механизмы кардинально отличаются.
Измерение в классическом Android (ViewGroup)
ViewGroup — это контейнер, который содержит другие View и отвечает за их измерение и расположение.
Процесс измерения:
- Measure Pass — родитель передает constraints (ширина, высота)
- View вызывает
setMeasuredDimension()для себя onMeasure()рекурсивно вызываетmeasure()для всех children- Каждый child должен установить свой размер
Layout Pass — после измерения происходит расположение:
onLayout()получает финальные размеры родителя- Родитель вызывает
layout()для каждого child - Child получает свою позицию (left, top, right, bottom)
Код классического подхода:
class CustomViewGroup(context: Context) : ViewGroup(context) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var totalHeight = 0
var maxWidth = 0
// Измеряем всех children
for (i in 0 until childCount) {
val child = getChildAt(i)
measureChild(child, widthMeasureSpec, heightMeasureSpec)
totalHeight += child.measuredHeight
maxWidth = maxOf(maxWidth, child.measuredWidth)
}
setMeasuredDimension(maxWidth, totalHeight)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
var y = 0
for (i in 0 until childCount) {
val child = getChildAt(i)
child.layout(0, y, child.measuredWidth, y + child.measuredHeight)
y += child.measuredHeight
}
}
}
Измерение в Jetpack Compose (Layout)
Layout — это композиция, которая определяет, как расположить children.
Процесс измерения:
- Measure Pass — передаются constraints как
Constraintsобъект - Каждый composable вызывает
layout()ламбду - Layout-ламбда получает результаты измерения children
- Родитель сразу же расположит child (нет отдельного layout pass)
Ключевые отличия:
- Single Pass Layout — вместо двух pass (measure + layout) одна
- Декларативность — вместо imperative кода используются ламбды
- Immutable — нет изменяемого состояния (в отличие от View)
Код Compose подхода:
@Composable
fun CustomLayout(content: @Composable () -> Unit) {
Layout(content = content) { measurables, constraints ->
val placeables = measurables.map { child ->
child.measure(constraints)
}
var totalHeight = 0
var maxWidth = 0
placeables.forEach { placeable ->
totalHeight += placeable.height
maxWidth = maxOf(maxWidth, placeable.width)
}
layout(maxWidth, totalHeight) {
var y = 0
placeables.forEach { placeable ->
placeable.place(0, y)
y += placeable.height
}
}
}
}
Сравнение
| Аспект | ViewGroup | Layout (Compose) |
|---|---|---|
| Pass | Двухфазный (measure + layout) | Однофазный |
| API | onMeasure(), onLayout() | Layout() ламбда |
| State | Изменяемое (View переиспользуется) | Immutable (переиспользуется функция) |
| Performance | Рекомпилкация при изменении | Recomposition при изменении state |
| Данные | MeasureSpec (int флаги) | Constraints (специальный класс) |
| Расчеты | Вычисления в onLayout после measure | Расчеты сразу в layout ламбде |
Практическое значение
Compose имеет лучшую производительность благодаря:
- Одному pass вместо двух
- Лучшей типизации (Constraints вместо MeasureSpec)
- Явной связи между measurement и placement
- Отсутствием необходимости в переиспользовании View
Для начинающих: Compose скрывает сложность двухфазной системы ViewGroup, предоставляя более интуитивный и безопасный API.