Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Compose: Теория

Обновлено 25 января 2024

Compose использует специальные composable-функции для построения приложений. С помощью функций вы описываете фигуры и другие данные для создания визуального интерфейса. Функции снабжаются аннотацией @Composable.

Большинство таких функций ничего не возвращают, точнее возвращают Unit(), который можно опустить. В реальности полная запись подобных функций выглядит следующим образом.


@Composable
fun MainScreen(
    modifier: Modifier = Modifier
): Unit {
}

Три фазы Compose

У Compose есть три главные фазы, которые следуют друг за другом.

State

  • Composition - что создаём? Например, две кнопки и текстовую метку
  • Layout - где размещаем? Нужно определиться, в какой последовательности разместим созданные компоненты - столбиком, в одну линию, наложить друг на друга
  • Draw - как показать? Внешний вид компонентов, которые показываются пользователю. Например, красная и зелёная кнопка и текстовая метка с обводкой

Не менее важная часть - рекомпозиция, т.е. перерисовка UI при изменении входных данных. Умная рекомпозиция — это обновление только тех частей UI, для которых данные изменились.

Compose устроен таким образом, что может пропустить какие-то фазы при обновлении данных, снижая нагрузку, если обновления не затрагивают определённую часть интерфейса. Но по неопытности, можно наоборот усугубить положение, неправильно выстроив логику приложения.

Например, можно улучшить приложение, если обновлять цвет в фазе Draw, минуя другие фазы. Для этой цели подойдёт модификатор drawBehind() вместо модификатора background().

Чуть подробнее про рекомпозицию. Допустим в Column у нас есть два элемента - Text и Icon. Мы решили поменять изображение в значке. Рекомпозиция будет вызвана только у Icon, но Text и Column затронуты не будут.

Это был простейший вариант. Но бывает так, что мы подаём на вход сложный объект. И хотя мы хотим поменять только данные в одном компоненте, рекомпозиция может произойти и в других местах. Такие моменты нужно отслеживать самостоятельно через различные инструменты.

Под капотом компилятор помечает функции как skippable, если можно пропустить рекомпозицию. Казалось бы, для оптимизации нам нужно добиваться, чтобы все функции были skippable. Но на самом деле не от каждой функции надо этого добиваться.

Для каждой skippable-функции генерируется больше кода, чем для non-skippable. Дополнительный код отслеживает её состояние и принимает решение о рекомпозиции, выполняя больше проверок и увеличивя размер приложения. В целом преимущества skippable-функций в виде уменьшения ненужных рекомпозиций перевешивают эти небольшие накладные расходы. Но необязательно добиваться skippable, если ваша функция:

  • только вызывает другие функции
  • редко использует рекомпозиции
  • содержит настолько сложную логику, что проще сделать рекомпозицию, чем проверять, изменились ли все параметры

Для всех остальных вариантов мы должны добиваться от функции поведения skippable. Компилятор пометит функцию как skippable, если все её параметры неизменяемы и стабильны.

Стабильными считаются:

  • Примитивные типы данных (Int, Float, Boolean) и String
  • Неизменяемые коллекции (kotlinx.collections.immutable)
  • Классы с неизменяемыми и стабильными параметрами
  • Классы и интерфейсы помеченные аннотациями @Stable и @Immutable
  • Лямбды

Для проверки рекомпозиций можно использовать Layout Inspector.

Миграция с Material 2 на Material 3

Изначально Compose использовал Material Design, который сокращённо называют Material 2. В настоящее время рекомендуется использовать Material Design 3 (или Material 3 или M3). При этом API в некоторых случаях сильно изменился и старый код не подходит, приходится переписывать примеры заново.

Настоятельно не рекомендуется смешивать классы из разных пакетов androidx.compose.material и androidx.compose.material3. Возможны ситуации, что в Material 3 ещё нет компонентов, которые были в Material 2, тогда переходить на M3 не стоит. В остальных случаях нужно мигрироовать на новую систему, а новые приложения сразу писать с использованием M3.

Несколько классов из старой Material 2 не изменились с появлением Material 3 и их можно использовать как прежде.

  • androidx.compose.material:material-icons-*:
  • androidx.compose.material:material-ripple:

Обратите внимание, что хотя название MaterialTheme осталось прежним, полное имя всё-таки другое - import androidx.compose.material3.MaterialTheme. Это важно, так как названия параметров у них различаются.

Поменялись названия у androidx.compose.material3.Typography.

M2M3
h1displayLarge
h2displayMedium
h3displaySmall
N/AheadlineLarge
h4headlineMedium
h5headlineSmall
h6titleLarge
subtitle1titleMedium
subtitle2titleSmall
body1bodyLarge
body2bodyMedium
captionbodySmall
buttonlabelLarge
N/AlabelMedium
overlinelabelSmall

Вместо BackdropScaffold теперь следует использовать Scaffold или BottomSheetScaffold.

Вместо BottomDrawer следует использовать ModalBottomSheet.

Поменялись названия у некоторых компонентов.

androidx.compose.material.BottomNavigation -> androidx.compose.material3.NavigationBar
androidx.compose.material.BottomNavigationItem -> androidx.compose.material3.NavigationBarItem
androidx.compose.material.Chip -> androidx.compose.material3.AssistChip/androidx.compose.material3.SuggestionChip
androidx.compose.material.ModalBottomSheetLayout -> androidx.compose.material3.ModalBottomSheet
androidx.compose.material.ModalDrawer -> androidx.compose.material3.ModalNavigationDrawer.

Но у большинства компонентов изменилось только полное название пакетов, например, было androidx.compose.material.Button, стало androidx.compose.material3.Button.

Scaffold сильно переработали. Вместо параметра backgroundColor теперь используется containerColor. Также убрали ScaffoldState, который использовали для параметра drawerState, так как этот параметр больше не нужен. Для показа Snackbar используется теперь SnackbarHostState. Все drawerXX-параметры удалены, для выдвижной панели используйте ModalNavigationDrawer.

Немного изменился TopAppBar, вдобавок появились родственники - CenterAlignedTopAppBar, MediumTopAppBar, LargeTopAppBar.

Поменялись внешний вид, размеры, цвета у многих компонентов - кнопки, переключатели и т.п.

Дополнительные материалы

Реклама