Хабрахабр

[Перевод] Погружение в Jetpack Compose

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

Пробуем новый UI-фреймворк для Android-приложений

В течение последних нескольких лет, участвуя во многих мобильных проектах, мне приходилось использовать различные технологии, например, такие как Android, ReactNative и Flutter. Переключение с ReactNative обратно на классический Android вызвало у меня смешанные чувства. Возвращение к Kotlin прошло хорошо, но я очень скучал по UI-фреймворку React. Небольшие повторно используемые компоненты, с помощью которых создается пользовательский интерфейс, великолепны и обеспечивают большую гибкость и скорость разработки.

Из-за этого трудно по-настоящему посвятить себя компонентному подходу. Вернувшись в классический Android, мне нужно было беспокоится о том, чтобы сохранить иерархию View как можно более однообразной. В конечном итоге мы удерживаем себя от экспериментов с пользовательским интерфейсом, которые могли бы улучшить UX. Это делает копипаст более заманчивым, что приводит к более сложному и менее поддерживаемому коду.

Иллюстрация: Эмануэль Багилла (Emanuel Bagilla)
Android раскрывает Jetpack Compose.

Jetpack Compose спешит на помощь

Поэтому после просмотра What’s new in Android с конференции Google I/O 2019 я сразу же начал разбираться с Compose и постарался больше узнать о нем. Compose — это инструментарий реактивного пользовательского интерфейса, полностью разработанный на Kotlin. Compose выглядит очень похоже на существующие фреймворки пользовательских интерфейсов, такие как React, Litho или Flutter.

Jetpack Compose стремится начать все с начала с учетом философии современных компонентов. Нынешняя структура UI-фреймворка Android существует с 2008 года, и со временем стала более сложной, ее довольно тяжело поддерживать. Фреймворк написан с учетом следующих основных целей:

  • Несвязанность с релизами платформы: Это позволяет быстро исправлять ошибки, поскольку Compose не зависит от новых релизов Android.
  • Меньший стек технологий: Фреймворк не заставляет вас использовать View или Fragment при создании пользовательского интерфейса. Все элементы являются компонентами и свободно могут быть составлены вместе.
  • Прозрачные управление состояниями и обработка событий: Одна из самых важных и сложных вещей, которую необходимо решать в больших приложениях, — это обработка потока данных и состояния в вашем пользовательском интерфейсе. Compose проясняет кто отвечает за состояние и как должны обрабатываться события, подобно тому, как это обрабатывает React.
  • Написание меньшего количества кода: Написание пользовательского интерфейса в Android обычно требует большого количества кода, особенно при создании более сложных layout’ов, например, с помощью RecyclerView. Compose призван значительно упростить способ создания пользовательского интерфейса.

Это облегчает создание изолированных и повторно используемых компонентов, что упрощает создание нового экрана с существующими элементами. Помогая вам, как разработчику, сосредоточиться на создании удобного пользовательского интерфейса, вместо того чтобы пытаться контролировать View-иерархию и приручать View и Fragment.

Простое приложение с Compose: Hello World

Давайте посмотрим на код простого приложения «Hello World» с Jetpack Compose.

class ComposeActivity : Activity() } } @Composable fun MyApp() { MaterialTheme { Text(text = "Hello world!", style = +themeTextStyle { h3 }) } }
}

В методе onCreate мы задаем содержимое нашего приложения, вызывая setContent. Это метод, который инициализирует составное дерево виджетов и оборачивает его в FrameLayout.

CraneWrapper отвечает за настройку поставщиков для Context, FocusManager и TextInputService. Чтобы все заработало, нам нужно обернуть наше приложение в CraneWrapper и MaterialTheme. Имея это в виду, мы можем добавить компонент Text, который будет отображать наш текст на экране в определенном стиле. MaterialTheme необходим для предоставления цветов, стилей и шрифтов ваших виджетов.

Введение состояния

Управление потоком данных и состояниями может быть сложной задачей. Чтобы проиллюстрировать, насколько это легко с Compose, давайте создадим простое приложение-счетчик.
Для работы с состояниями Jetpack Compose использует идеи других современных UI-фреймворков, таких как Flutter и React. Существует однонаправленный и реактивный поток данных, который заставляет ваш виджет обновляться или «перекомпоновываться».

@Composable
fun MyApp() { MaterialTheme { Counter() }
} @Composable
fun Counter() { val amount = +state { 0 } Column { Text(text = "Counter demo") Button(text = "Add", onClick = { amount.value++ }) Button(text = "Subtract", onClick = { amount.value-- }) Text(text = "Clicks: ${amount.value}") }
}

Как вы можете видеть в примере ниже, обновляя состояние «amount», виджеты разумно перекомпоновываются при изменении состояния. В примере выше мы добавляем кнопки «Add» и «Subtract» вместе с лейблом, отображающим текущее количество нажатий.


Запуск демо-приложения

Пытаясь выяснить, что это за колдовство, я залез в исходный код. Состояние amount инициализируется с помощью +state { 0 }. Это мое мнение, хотя я все еще не уверен, что до конца все понимаю.

Класс Effect является нечетким классом, который содержит блок исполняемого кода, который выполняется позиционно в контексте композиции. state {...} создает Effect<State<T<code>>. Оператор + является временным оператором, который разрешает State из Effect. Класс State содержит одно значение с типом Model, по сути делая это значение наблюдаемым (observable).

Пользовательские модели состояний

Вместо использования +state {} для создания модели отдельного значения мы также можем создать пользовательскую модель с помощью аннотации @Model. Мы можем улучшить наше приложение-счетчик, разделив его на более мелкие виджеты и передав модель другим виджетам, которые обновляются и отображают состояние этой модели.

@Model
class CounterModel { var counter: Int = 0 var header = "Counter demo" fun add() { counter++ } fun subtract() { counter-- }
}

Давайте обновим наш виджет, чтобы использовать CounterModel: Используя аннотацию @Model, плагин Compose Compiler делает все переменные в вашей модели наблюдаемыми, чтобы их можно было использовать для перекомпоновки виджетов.

@Composable
fun Counter(counterModel: CounterModel) { Column { CounterHeader(counterModel) AddSubtractButtons(counterModel) CounterLabel(counterModel) }
} @Composable
fun CounterHeader(counterModel: CounterModel) { Text(text = counterModel.header)
} @Composable
fun AddSubtractButtons(counterModel: CounterModel) { Button( text = "Add", onClick = { counterModel.add() }) Button( text = "Subtract", onClick = { counterModel.subtract() })
} @Composable
fun CounterLabel(counterModel: CounterModel) { Text(text = "Clicks: ${counterModel.counter}")
}

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

Больше никаких view

Важно понимать, что виджеты Jetpack Compose не используют под капотом view или fragment, это всего лишь функции, которые рисуют на холсте. Плагин Compose Compiler обрабатывает все функции с аннотацией @Composable и автоматически обновляет UI-иерархию.

Глядя на исходный код DrawFillRect, становится ясно, что он рисует линии прямо на холсте. Например, виджет Divider состоит из виджета Padding, который содержит виджет DrawFillRect. Все остальные виджеты реализованы таким же образом.

@Composable
private fun DrawFillRect(brush: Brush) { Draw { canvas, parentSize -> val paint = Paint() brush.applyBrush(paint) canvas.drawRect(parentSize.toRect(), paint) }
}

Мы видим FrameLayout, содержащий CraneWrapper, который мы создали в коде, оттуда на экране отображается иерархия Compose UI. Исходный код DrawFillRect, который используется внутри виджета Divider
Если мы посмотрим на Layout Inspector, запустив один из примеров приложений от Google, ясно увидим, что при запуске приложения Android с Compose нет никаких View или ViewGroups.


Layout Inspector инспектирует приложение Jetpack Compose.

Button, и должен создавать все виджеты с нуля. Отсутствие views также означает, что Jetpack Compose не может использовать доступные в настоящее время view, такие как android.widget. Это одна из причин почему Jetpack Compose понадобится время, прежде чем он будет готов к использованию в продакшене. Если взглянуть, например, на Flutter, в котором используется тот же подход, то можно увидеть, что это тяжелая работа.

Все элементы являются виджетами

Совсем как у Flutter, в Compose все элементы — это виджеты. Более сложные виджеты были разбиты на элементарные виджеты с четкими обязанностями. Поэтому даже padding, spacers и так далее являются виджетами. Например, если вы хотите добавить отступ вокруг кнопки, просто оберните ее в padding виджет:

Padding(padding = 16.dp) { Button(text = "Say hello", onClick = { ... })
}

Соединение кода с пользовательским интерфейсом

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

Column { listOf("John", "Julia", "Alice", "Mark").forEach { Text(text = it) }
}

Это действительно мощная фича, но вы должны быть осторожны, чтобы не запрограммировать слишком много логики на уровне пользовательского интерфейса.

Совместимость с вашими Android-приложениями

Compose разработан таким образом, что вы можете добавить его в уже существующее приложение и постепенно перенести некоторые части вашего UI в новый фреймворк. Приведенные выше примеры добавляют Jetpack Compose UI к одному activity. Также вы можете встроить Compose-виджеты в существующий XML layout с помощью аннотации GenerateView:

@Composable
@GenerateView
fun Greeting(name: String) { /* … */ }
// В каком-то существующем layout.xml
<GreetingView app:name=”John” />

Заключение

Я в восторге от Compose, потому что он уменьшает растущие страдания, которую я испытываю при разработке под Android. Он помогает быть более гибким, фокусироваться на создании удобного пользовательского интерфейса, а четкая ответственность также позволяет избежать ошибок.

Тем не менее, я думаю, что сейчас подходящий момент, чтобы взглянуть на Jetpack Compose. У Compose впереди долгий путь, на мой взгляд, его можно будет использовать в продакшене не раньше чем через год или два. Все отзывы помогут улучшить этот новый фреймворк. Создатели активно ищут фидбек, на этом этапе все еще можно вносить изменения.

Также, я думаю, вам очень интересно будет посмотреть видео по шаблонам декларативного интерфейса с Google I/O.
Я с нетерпением жду, когда смогу использовать Compose в реальных Android-приложениях! Прочитайте мою статью «Try Jetpack Compose today», чтобы узнать, как подключить пре-альфа Compose.

Ждем ваши комментарии и отличных всем выходных! Вот и все.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть