Irbisa · Шпаргалка

Основы Jetpack Compose

Composable-функции, модификаторы, layout, рекомпозиция, Material 3

Junior-разработчик · 23 вопроса

  1. 01

    Что такое Jetpack Compose и чем он отличается от View-системы?

    Jetpack Compose представляет собой декларативный UI-тулкит Android: вы пишете Kotlin-функции с @Composable, которые описывают UI для текущего состояния, а Compose вызывает их заново, когда это состояние меняется.

    @Composablefun Greeting(name: String, onClick: () -> Unit) {  Column(Modifier.padding(16.dp)) {    Text("Hello, $name", style = MaterialTheme.typography.headlineSmall)    Button(onClick = onClick) { Text("Say hi") }  }…
  2. 02

    Что такое composable-функция и почему @Composable-функцию можно вызвать только из другой composable-функции?

    Composable называют Kotlin-функцию с аннотацией @Composable, которая не возвращает UI, а эмитит его в композицию.

  3. 03

    Что такое рекомпозиция, когда она происходит и как Compose решает, что перезапустить?

    Рекомпозицией называют повторный вызов composable-функций, когда прочитанное ими состояние изменилось, чтобы UI соответствовал новому состоянию.

  4. 04

    Что такое Modifier в Compose и почему важен порядок модификаторов?

    Modifier представляет собой упорядоченную неизменяемую цепочку декораций (размер, отступы, фон, обработка нажатий, семантика), применяемую к composable-функции.

    // Red covers only the inner area; the 16dp padding stays transparentText("A", Modifier.padding(16.dp).background(Color.Red)) // Red covers the padding as wellText("B", Modifier.background(Color.Red).padding(16.dp))…
  5. 05

    Как Row, Column и Box располагают дочерние элементы и как управлять отступами, выравниванием и весом?

    Column складывает элементы вертикально, Row складывает горизонтально, а Box кладёт их друг на друга.

    @Composablefun ProductRow(p: Product, modifier: Modifier = Modifier) {  Row(    modifier = modifier.fillMaxWidth().padding(12.dp),    verticalAlignment = Alignment.CenterVertically,    horizontalArrangement = Arrangement.spacedBy(12.dp),…
  6. 06

    Чем LazyColumn отличается от Column и как LazyColumn соотносится с RecyclerView?

    Column компонует и измеряет все дочерние элементы сразу, а LazyColumn обрабатывает только видимые элементы плюс несколько следующих.

    @Composablefun MessageList(messages: List<Message>, modifier: Modifier = Modifier) {  val state = rememberLazyListState()  LazyColumn(    modifier = modifier.fillMaxSize(),    state = state,…
  7. 07

    Как написать переиспользуемую composable-функцию, чтобы вызывающий сам решал, что будет внутри?

    Используйте слот: параметр типа @Composable () -> Unit, который компонент вызывает там, где должно быть содержимое вызывающего.

    @Composablefun SettingRow(  title: String,  modifier: Modifier = Modifier,  leading: (@Composable () -> Unit)? = null,       // optional slot  trailing: @Composable RowScope.() -> Unit = {},  // scoped slot…
  8. 08

    Как работает тематизация с Material 3 в Compose?

    Тема Material 3 задаётся одним composable, MaterialTheme, который хранит цветовую схему, типографику и формы; каждый Material-компонент под ним читает их через MaterialTheme.colorScheme, .typography и .shapes.

    private val LightColors = lightColorScheme(primary = Color(0xFF1565C0), onPrimary = Color.White)private val DarkColors = darkColorScheme(primary = Color(0xFF90CAF9), onPrimary = Color(0xFF003258)) @Composablefun AppTheme(  dark: Boolean = isSystemInDarkTheme(),…
  9. 09

    Что такое Scaffold, какие слоты он даёт и почему для показа snackbar нужна корутина?

    Scaffold раскладывает экран Material 3 по слотам (topBar, bottomBar, floatingActionButton, snackbarHost) и передаёт оставшуюся область контента как PaddingValues.

    @Composablefun InboxScreen(messages: List<Message>, onArchive: (Message) -> Unit) {  val hostState = remember { SnackbarHostState() }  val scope = rememberCoroutineScope()   Scaffold(…
  10. 10

    Как собрать форму входа в Compose: состояние TextField, KeyboardOptions, IME-действие и переход фокуса к следующему полю?

    Каждое поле представляет собой OutlinedTextField, текст которого хранится в вашем состоянии.

    @Composablefun LoginForm(onSubmit: (email: String, password: String) -> Unit) {  var email by rememberSaveable { mutableStateOf("") }  var password by rememberSaveable { mutableStateOf("") }  val focusManager = LocalFocusManager.current  val submit = {…
  11. 11

    Как работают функции @Preview и как сделать экран пригодным для превью?

    @Preview помечает composable, который Android Studio отрисовывает без запуска приложения: без Activity, без ViewModel, без DI-графа.

    @Composablefun HomeRoute(viewModel: HomeViewModel = hiltViewModel()) {  val state by viewModel.state.collectAsStateWithLifecycle()  HomeScreen(state = state, onRefresh = viewModel::refresh)}…
  12. 12

    Новую фичу вы бы писали на Compose или на XML, и какие у Compose реальные ограничения в продакшене?

    Для новой фичи в 2026 году Compose является выбором по умолчанию: меньше кода, UI управляется состоянием, а новые UI-API (Material 3, адаптивные раскладки) Google выпускает сначала для Compose.

  13. 13

    Из каких трёх фаз состоит кадр в Compose (composition, layout, drawing), и почему изменение состояния может вообще не запускать composition?

    Кадр проходит до трёх фаз по порядку: composition решает, что показать, layout измеряет и размещает каждый узел, drawing его рисует.

    // read in composition: every animation frame recomposes DotSlow before it is redrawn@Composablefun DotSlow(selected: Boolean) {  val color by animateColorAsState(if (selected) Color.Red else Color.Gray, label = "dot")  Box(Modifier.size(24.dp).background(color))}…
  14. 14

    Каждый Box ниже отличается только цепочкой модификаторов. Какого он размера и какая его часть закрашена?

    Column {  // A  Box(Modifier.size(100.dp).size(50.dp).background(Color.Red))  // B  Box(Modifier.size(50.dp).padding(10.dp).background(Color.Green))  // C  Box(Modifier.padding(10.dp).size(50.dp).background(Color.Blue))  // D  Box(    Modifier.width(200.dp).requiredWidth(80.dp)      .height(40.dp).background(Color.Gray)  )}

    A будет 100×100 dp, весь красный; B будет 50×50 с зелёным центром 30×30; C будет 70×70 с синим квадратом 50×50 и отступом 10 dp; D занимает слот шириной 200 dp, а серый блок 80×40 стоит в нём по центру.

  15. 15

    Найдите баг: пользователь раскрывает одно сообщение, сверху приходит новое, и раскрытым оказывается уже другое сообщение.

    @Composablefun InboxList(messages: List<Message>, onOpen: (Message) -> Unit) {  LazyColumn {    items(messages) { message ->      var expanded by remember { mutableStateOf(false) }      MessageRow(        message = message,        expanded = expanded,        onToggle = { expanded = !expanded },        onOpen = { onOpen(message) },      )    }  }}

    У items(messages) нет key, поэтому remember-состояние каждой строки привязано к её позиции, а не к сообщению.

  16. 16

    Какой жизненный цикл у composable и как из Compose реагировать на события жизненного цикла Activity?

    Composable входит в композицию, рекомпозируется ноль или больше раз и покидает её.

    @Composablefun MapScreen(viewModel: MapViewModel) {  // Stops collecting when the app goes below STARTED  val state by viewModel.state.collectAsStateWithLifecycle()   // Start on ON_START, stop on ON_STOP or when leaving the composition…
  17. 17

    Найдите баг: по логам бэкенда этот экран продолжает дёргать API счёта каждые 10 секунд после того, как пользователи нажали Home.

    @Composablefun LiveScoresScreen(viewModel: ScoresViewModel) {  val scores by viewModel.scores.collectAsStateWithLifecycle()   LaunchedEffect(Unit) {    while (true) {      viewModel.refresh()      delay(10.seconds)    }  }   ScoreList(scores)}

    LaunchedEffect привязан к композиции, а не к жизненному циклу Activity.

  18. 18

    Как работать с edge-to-edge и window insets в Compose и что изменилось для приложений с таргетом Android 15 и 16?

    Приложения с таргетом Android 15 (API 35) рисуются edge-to-edge, поэтому контент оказывается под статус-баром и навигационной панелью, пока вы не примените window insets, через innerPadding у Scaffold или inset-модификаторы вроде safeDrawingPadding().

    class MainActivity : ComponentActivity() {  override fun onCreate(savedInstanceState: Bundle?) {    enableEdgeToEdge()    super.onCreate(savedInstanceState)    setContent { AppTheme { InboxRoute() } }  }…
  19. 19

    Как адаптировать экран на Compose под планшеты, складные устройства и окна изменяемого размера, и что Android 16 меняет для приложений с фиксированной ориентацией?

    Выбирайте раскладку по классу размера окна, а не по устройству: читайте currentWindowAdaptiveInfoV2().windowSizeClass и переключайте раскладку на ширинах 600 dp и 840 dp.

    @Composablefun MailApp(tabs: List<Tab>, mails: List<Mail>) {  var current by rememberSaveable { mutableIntStateOf(0) }  val listState = rememberLazyListState()   // above the branch: an unfold keeps the scroll  val wide = currentWindowAdaptiveInfoV2().windowSizeClass    .isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND)…
  20. 20

    Как работает Navigation Compose и как передавать аргументы между экранами?

    Navigation Compose хранит back stack composable-destination внутри NavHost, которым управляет NavController.

    @Serializable data object Home@Serializable data class Profile(val id: Long) @Composablefun AppNavHost() {  val navController = rememberNavController()…
  21. 21

    Как показать Android View внутри Compose и composable-функцию внутри существующего XML-экрана?

    AndroidView встраивает View в composable, а ComposeView встраивает composable в иерархию View.

    @Composablefun WebPage(url: String, modifier: Modifier = Modifier) {  AndroidView(    modifier = modifier,    factory = { context -> WebView(context) },   // runs once    update = { view -> view.loadUrl(url) },      // re-runs when url changes…
  22. 22

    Что такое дерево семантики в Compose и как его используют сервисы доступности и UI-тесты?

    Рядом с деревом UI Compose строит дерево семантики, которое описывает смысл каждого элемента: текст, роль, состояние, доступные действия.

    @Composablefun ProductRow(p: Product, onOpen: () -> Unit) {  Row(    Modifier      .clickable(onClick = onOpen)          // one node with a click action      .testTag("product-${p.id}")…
  23. 23

    Row, Column и Box не выражают нужную раскладку. Как написать свою и какое правило нельзя нарушать?

    Composable Layout даёт вам дочерние элементы как measurable плюс входящие ограничения; вы измеряете каждый элемент, выбираете свой размер и расставляете элементы.

    @Composablefun TwoColumnGrid(modifier: Modifier = Modifier, content: @Composable () -> Unit) {  Layout(content = content, modifier = modifier) { measurables, constraints ->    val columnWidth = constraints.maxWidth / 2    val childConstraints = constraints.copy(minWidth = 0, maxWidth = columnWidth)    val placeables = measurables.map { it.measure(childConstraints) }   // once each…