- Основы Jetpack Compose: Урок №1
- Что такое Jetpack Compose
- Урок 1: Composable функции
- Добавляем текстовый элемент
- Определите Composable функцию
- Предварительный просмотр вашей функции в Android Studio
- Введение в Jetpack Compose
- Что такое Jetpack Compose
- Установка средств разработки
- Погружение в JetPack Compose. Часть 1/2
- Какие проблемы решает Compose?
- Устройство Composable-функции
- Декларативный UI
- Композиция vs Наследование
- Инкапсуляция
- Перекомпоновка (перерисовка компонентов)
- Заключительные мысли
Основы Jetpack Compose: Урок №1
В этом руководстве вы создадите простой пользовательский интерфейс с помощью декларативных функций Jetpack Compose.
Что такое Jetpack Compose
Jetpack Compose — это современный набор инструментов для создания собственного пользовательского интерфейса в Android-приложении. Этот декларативный фреймворк упрощает и ускоряет разработку пользовательского интерфейса на Android с меньшим количеством кода, мощными инструментами и интуитивно понятными API-интерфейсами Kotlin.
В этом руководстве вы создадите простой пользовательский интерфейс с помощью декларативных функций. Вы не будете редактировать макеты XML или напрямую создавать виджеты пользовательского интерфейса. Вместо этого вы вызовете функции Jetpack Compose, чтобы сказать, какие элементы вы хотите видеть, а компилятор Compose сделает все остальное.
Примечание: Jetpack Compose в настоящее время находится в альфа-версии. API еще не доработаны, изменения запланированы и возможны.
Урок 1: Composable функции
Фреймворк работает на основе Composable функций. Эти функции позволяют программно определять пользовательский интерфейс вашего приложения, описывая его форму и зависимости, а не сосредотачиваться на процессе создания пользовательского интерфейса. Чтобы создать “составную” функцию, просто добавьте аннотацию @Composable к имени функции.
Добавляем текстовый элемент
Для начала следуйте инструкциям по настройке Jetpack Compose и создайте приложение, используя шаблон Empty Compose Activity. Шаблон по умолчанию уже содержит некоторые элементы Compose, но давайте создадим его шаг за шагом. Сначала удалите функции Greeting и Default Preview и удалите блок setContent из MainActivity, оставив действие пустым. Скомпилируйте и запустите пустое приложение.
Теперь добавьте текстовый элемент в пустое действие. Вы делаете это, определяя блок содержимого и вызывая функцию Text().
Блок setContent определяет макет активити. Вместо определения содержимого макета с помощью XML-файла, мы вызываем composable функции. Jetpack Compose использует кастомный плагин компилятора Kotlin для преобразования этих составных функций в элементы пользовательского интерфейса приложения. Например, функция Text() определяется библиотекой Compose UI — вы вызываете эту функцию, чтобы объявить текстовый элемент в своем приложении.
Определите Composable функцию
Составные функции могут вызываться только в рамках других составных функций. Чтобы сделать функцию составной, добавьте аннотацию @Composable. Чтобы попробовать это, определите функцию Greeting(), которой передается имя и которая использует это имя для настройки текстового элемента.
Предварительный просмотр вашей функции в Android Studio
Текущая canary сборка Android Studio позволяет вам предварительно просматривать ваши составные функции прямо в IDE, вместо того, чтобы загружать приложение на устройство Android или в эмулятор. Основное ограничение заключается в том, что составная функция не должна принимать никаких параметров. По этой причине вы не можете предварительно просмотреть функцию Greeting() напрямую. Вместо этого создайте вторую функцию с именем PreviewGreeting(), которая вызывает Greeting() с соответствующим параметром.
Добавьте аннотацию @Preview перед @Composable.
Пересоберите свой проект. Само приложение не поменяется, поскольку новая функция previewGreeting() нигде не вызывается, но Android Studio добавляет окно предварительного просмотра. В этом окне отображается предварительный просмотр элементов пользовательского интерфейса, созданных Composable функцией, отмеченной аннотацией @Preview. Чтобы обновить предварительный просмотр в любое время, нажмите кнопку обновления в верхней части окна предварительного просмотра.
Источник
Введение в Jetpack Compose
Что такое Jetpack Compose
Jetpack Compose представляет современный тулкит от компании Google для создания приложений под ОС Android на языке программирования Kotlin. Jetpack Compose упрощает написание и обновление визуального интерфейса приложения, предоставляя декларативный подход.
Операционной системе Android более 10 лет. За этот период API и библиотеки для создания приложений под эту ОС много раз обновлялись, дополнялись, одни API устаревали, другие, наоборот, добавлялись в арсенал разработчиков. Но в этоге подобное развитие привело к усложнению платформы. Чтобы упростить разработку, сделать ее более быстрой, простой, упростить поддержку компания Google в мае 2019 года анонсировала новый тулкит — Jetpack Compose . В августе 2020 вышла первая альфа-версия тулкита. А 28 июля 2021 года вышла первая стабильная версия — Jetpack Compose 1.0 , которая является текущей на момент написания данной статьи и которая применяется далее в дальнейших статьях данного руководства.
Jetpack совместим с существующим набором библиотек Android, которые можно использовать в стандартных проектах на Java и Kotlin для написания приложений под Android. Отличительной же чертой Jetpack Compose является то, что он предлагает кардинально другой подход к созданию приложений.
Прежде всего, Jetpack Compose предлагает использовать язык Kotlin и все его преимущества. Соответственно для работы с тулкитом необходимо иметь базовые знания данного языка. Для этого можно обратиться к руководству по языку Kotlin на этом сайте.
Jetpack уменьшает объем кода.
Jetpack Compose предлагает декларативный API, который является более интуитивным.
Ключевой концепцией тулкита Jetpack Compose является composable -функция (функция, которая имеет аннотацию @Composable ). Такие функции представляют некоторые части визуального интерфейса, из которых строится приложение. Это упрощает построение и обновление сложных интерфейсов, тестирование и поддержку самих компонентов
Установка средств разработки
Существуют разные среды разработки для Android. Рекомендуемой средой разработки является Android Studio , которая создана специально для разработки под ОС Android. Поэтому мы ее и будем использовать. Загрузить файл установщика можно с официального сайта: https://developer.android.com/studio:
Кроме самой среды Android Studio для разработки также потребуется набор инструментов, который называется Android SDK . Например, если ранее Android SDK еще не было установлено, то при первом обращении к Android Studio она сообщит, что Android SDK отсутствует.
Мы можем отдельно вручную загрузить Android SDK с официального сайта и установить его. Либо мы можем сделать это непосредственно из Android Studio. Так, нажмем на кнопку Next . И на следующем экране нам будет предложено загрузить Android SDK для последней версии API (в данном случае для Android 11):
Здесь же мы можем указать место для установки Android SDK, если путь по умолчанию нас не устраивает.
Нажмем на кнопку Next , и далее нам отобразится окно со сводкой того, что именно будет установлено:
Нажмем на кнопку Finish , чтобы, наконец, все это установить.
И после завершения установки нажмем на кнопку Finish . И мы можем приступать к созданию приложений.
Источник
Погружение в JetPack Compose. Часть 1/2
Собрал здесь лучшие статьи, библиотеки и проекты на Jetpack Compose:
Jetpack Compose Awesome
Ожидания по поводу разработки пользовательского интерфейса выросли. Сегодня мы не можем создать приложение и удовлетворить потребности пользователя, не имея отточенного пользовательского интерфейса, включая анимацию и движение UI-элементов. Этих требований не существовало при создании текущего UI Toolkit-а системы Android. Чтобы решить технические проблемы быстрого и эффективного создания безупречного пользовательского интерфейса, мы представили Jetpack Compose — современный набор инструментов для создания UI, который помогает разработчикам приложений добиться успеха на этом новом поприще.
В двух статьях мы расскажем о преимуществах Compose и посмотрим, как это работает «под капотом». Для начала в этом посте я расскажу о проблемах, которые решает Compose, о причинах некоторых наших дизайнерских решений и о том, как они помогают разработчикам приложений. Кроме того, я расскажу о ментальной модели Compose, о том, как вы должны думать о коде, который вы пишете в Compose, и о том, как вы должны формировать свой API.
Какие проблемы решает Compose?
Разделение ответственности (Separation of concerns) — это хорошо известный принцип разработки программного обеспечения. Это одна из фундаментальных вещей, которую мы, как разработчики приложений, узнаем. Несмотря на то, что этот принцип хорошо известен, часто трудно понять, соблюдается ли этот принцип на практике. Может быть полезно думать об этом принципе как о термине типа «сцепление» или «связанность».
Когда мы пишем код, мы создаем модули, которые состоят из нескольких сущностей (unit-тов). Связанность (Coupling) — это зависимость между сущностями в разных модулях, которая отражает то, ка части одного модуля влияют на части других модулей. Целостность (Cohesion)- это, наоборот, взаимосвязь между сущностями (юнитами) в модуле и показывает, насколько хорошо сгруппированы юниты в модуле.
При написании поддерживаемого программного обеспечения наша цель — минимизировать связанность и максимизировать целостность.
Когда у нас есть сильно связанные модули, внесение изменений в код в одном месте означает необходимость внесения множества изменений в другие модули. Что еще хуже, связь часто может быть неявной, так что вещи ломаются в неожиданных местах из-за изменения, которое кажется совершенно не связанным.
Разделение ответственности заключается в том, чтобы сгруппировать как можно больше связанного кода, чтобы наш код можно было легко поддерживать и масштабировать по мере роста приложения.
Давайте перейдем к практике, и рассмотрим современные подходы решения этого вопроса в мире Android-разработки. Возьмем для примера ViewModel и XML-лейаут.
ViewModel предоставляет данные лейауту. Оказывается, здесь может быть спрятано много зависимостей: большая взаимосвязь между ViewModel и лейаутом. Один из наиболее частых и х хорошо знакомых нам случаев сильной взаимосвязи — это использование API (от Android или сторонних библиотек — прим. переводчика), в которых требуется знание о внутренностях самого XML-макета, например метод findViewByID .
Использование таких API требует знания того, как устроен XML-макет, и создает взаимосвязь между ними. Поскольку наше приложение со временем растет, мы должны следить за тем, чтобы ни одна из этих зависимостей не устарела.
Большинство современных приложений отображают пользовательский интерфейс динамически и меняются в процессе выполнения. В результате необходимо не только проверить, что эти зависимости (т. е. View-элементы) предоставляются XML-макетом, но также и то, что они будут предоставляться во время работы программы. Если элемент покидает иерархию View во время выполнения, некоторые из этих зависимостей могут быть нарушены и могут привести к таким проблемам, как NulReferenceExceptions .
Примечание переводчика:
Под «предоставлением зависимостей» имеется в виду наличие вьюшки в самом лейауте и возможность найти её через findViewById.
Обычно ViewModel определяется языке программирования Kotlin, а макет — в XML. Из-за этой разницы в языке существует принудительное разделение, хотя ViewModel и XML-макет иногда могут быть тесно связаны. Другими словами, они очень тесно связаны.
Возникает вопрос: что, если бы мы начали определять лейаут, т. е. структуру нашего пользовательского интерфейса на одном языке? Что, если мы выберем Kotlin?
Поскольку в этом случае мы будем работать на одном языке, некоторые из зависимостей, которые ранее были неявными, могут стать более явными. Мы также можем провести рефакторинг кода и переместить вещи туда, где они уменьшат взаимосвязь и увеличат согласованность.
Теперь вы можете подумать, что вы смешиваете логику с пользовательским интерфейсом. Реальность такова, что у вас будет логика, связанная с пользовательским интерфейсом, в вашем приложении, независимо от того, как оно структурировано. Сама структура не может этого изменить.
Но то, что может сделать фреймворк, — это предоставить вам инструменты, упрощающие разделение: этим инструментом является функция Composable. Функции — это то, что вы, вероятно, использовали в течение долгого времени для разделения задач в других местах вашего кода. Навыки, которые вы приобрели для такого рода рефакторинга и написания надежного, поддерживаемого, чистого кода, — те же навыки применимы и к Composable -функциям.
Устройство Composable-функции
Это пример Composable-функции.
Здесь функция получает данные как параметры из класса appData . В идеале это неизменяемые данные, которые Composable-функция не меняет: Composable-функция должна быть функцией преобразования для этих данных. Следовательно, мы можем использовать любой код на Kotlin, чтобы взять эти данные и использовать их для описания нашей иерархии, например вызвав функции Header() и Body() .
Это означает, что мы вызываем другие Composable-функции, и эти вызовы отражают структуру нашего UI. Мы можем использовать все примитивы предоставляемые Kotlin-ом. Мы можем включить операторы if и циклы for для управления структурой UI, чтобы справиться с более сложной логикой пользовательского интерфейса.
Composable-функции часто используют конечный лямбда-синтаксис Kotlin, поэтому Body() — это Сomposable-функция, которая принимает composable-лямбду в качестве параметра. Это подразумевает иерархию или структуру, поэтому Body() обертывает здесь набор элементов.
Декларативный UI
Декларативный — это модное, но важное слово. Когда мы говорим о декларативном программировании, мы говорим об его отличии от императивного программирования. Давайте рассмотрим пример.
Рассмотрим почтовое приложение со значком непрочитанных сообщений. Если сообщений нет, приложение отображает пустой конверт. Если есть какие-то сообщения, мы визуализируем бумагу в конверте, а если есть 100 сообщений, мы визуализируем значок, как будто он горит.
С императивным интерфейсом нам, возможно, придется написать такую функцию подсчета обновлений:
В этом коде мы получаем новое количество сообщений и должны выяснить, как обновить текущий пользовательский интерфейс, чтобы отразить это состояние. Здесь много корнер-кейсов, и эта логика непроста, хотя это относительно простой пример.
Если мы перепишем эту логику в декларативном стиле, мы получим нечто подобное:
Здесь мы говорим:
Если счет больше 99, покажи огонь ?.
Если счет больше 0, покажи бумагу
Если счетчик больше 0, отобрази значок счетчика
Это то, что подразумевается под декларативным API. Код, который мы пишем, описывает нужный нам пользовательский интерфейс, но не описывает, как перейти в это состояние. Важным здесь является то, что при написании декларативного кода, подобного этому, вам больше не нужно беспокоиться о том, в каком предыдущем состоянии был ваш пользовательский интерфейс, вам нужно только указать, каким должно быть ваше текущее состояние. Фреймворк контролирует, как перейти из одного состояния в другое, поэтому нам больше не нужно об этом думать.
Композиция vs Наследование
В разработке программного обеспечения композиция — это то, как несколько частей более простого кода могут объединяться в более сложный блок кода. В объектно-ориентированной модели программирования одной из наиболее распространенных форм композиции является наследование на основе классов. В мире Jetpack Compose, поскольку мы работаем только с функциями, а не с классами, метод композиции сильно отличается, но имеет много преимуществ перед наследованием. Давайте посмотрим на пример.
Допустим, у нас есть View и мы хотим создать поле ввода. В случае с наследованием наш код может выглядеть так:
View — это базовый класс. ValidatedInput является подклассом Input . Для проверки даты DateInput наследуется от ValidatedInput . Но тогда возникает проблема: мы хотим создать компонент с вводом диапазона дат, следовательно, нам нужно осуществлять проверку по двум датам — дате начала и дате окончания. Вы можете создать подкласс DateInput , но вам нужно сделать это дважды, а вы не можете этого сделать. Это ограничение наследования: у нас должен быть единственный родитель, от которого мы наследуем.
В Compose это не так сложно. Допустим, мы начинаем с базового composable-компонента Input :
Когда мы создаем наш ValidatedInput , мы просто вызываем Input в теле нашей функции. Затем мы можем дополнить его чем-нибудь для проверки.
Затем для DataInput мы можем напрямую вызвать ValidatedInput .
Теперь, когда мы сталкиваемся с вводом диапазона дат, у нас больше нет проблемы: это всего лишь два вызова вместо одного.
При создании UI-компонентов при помощи Compose, у них нет единственного родителя, и это решает проблему, которая возникла с в случае с использованием наследования.
Другой тип проблемы композиции — это абстрагирование от типа декотратора. Для примера рассмотрим следующий пример наследования:
FancyBox — это View, которое украшает другие View, в данном случае Story и EditForm . Мы хотим создать FancyStory и FancyEditForm , но как? Мы наследуем от FancyBox или мы наследуем от Story ? Это неясно, потому что, опять же, у нас может быть только один родитель в цепочке наследования.
Compose же справляется с этим очень хорошо.
У Composamble-функции есть лямбда, в которой мы описываем дочерние View, т. е. мы определяем View, которая обертывает другие View. Итак, теперь, когда мы хотим создать FancyStory , мы вызываем Story внутри FancyBox и можем сделать то же самое с FancyEditForm . Это способ композиции в Compose.
Инкапсуляция
Еще одна вещь, которую хорошо выполняет Compose — это инкапсуляция. Это то, о чем вы должны думать, когда делаете общедоступные API-интерфейсы Composable-функций: публичный API-интерфейс Composable-функций — это набор параметров, которые она получает, поэтому он не может их контролировать. С другой стороны, Composable-компонент может управлять состоянием и создавать его, а затем передавать это состояние вместе с любыми данными, которые он получил, в другие Composable-компонентыв качестве параметров.
Теперь, поскольку он управляет этим состоянием, если вы хотите изменить состояние, вы можете разрешить дочерним компонентам передавать сигнал об этом изменении с помощью коллбека.
Перекомпоновка (перерисовка компонентов)
Это способ сказать, что любую Composable-функцию можно повторно вызвать в любое время. Если у вас очень большая Composable-иерархия, когда часть вашей иерархии изменяется, вам не нужно пересчитывать всю иерархию. Т. к. Composable-функции можно вызывать повторно, вы можете использовать эту особенность для некоторых полезных вещей.
Например, вот функция bind , которую вы можете встретить сегодня в разработке для Android.
У нас есть LiveData , на которую мы хотим подписаться для обновления View. Для этого мы вызываем метод observe в классе, имеющем жизненный цикл (LifecycleOwner — Activity или Fragment), а затем передаем лямбду. Лямбда вызывается каждый раз при обновлении LiveData, и когда это происходит, мы хотим обновлять и View.
С помощью Compose мы можем изменить этот способ взаимодействия с LiveData:
Это аналогичный Composable-компонент Messages , который получает LiveData и вызывает compose-метод observationAsState . Метод observeAsState преобразует LiveData в State . Это означает, что вы можете использовать полученное значение в теле функции. Экземпляр State подписан на экземпляр LiveData, что означает, что он будет обновляться при каждом обновлении LiveData. Это также означает, что где бы ни читался экземпляр State , окружающая compose-функция, в которой он читается, будет автоматически подписываться на эти изменения. Конечным результатом является то, что больше нет необходимости указывать LifecycleOwner или коллбэк для обновления, поскольку Composable может неявно выполнять функцию их обоих.
Заключительные мысли
Compose предоставляет современный подход к созданию вашего UI, позволяя эффективно разделять ответственность в коде. Поскольку compose-функции очень похожи на обычные функции Kotlin, вы можете использовать те же самые инструменты для рефакторинга, что и для обычного Kotlin-кода.
В следующем посте я собираюсь сосредоточить внимание на некоторых деталях реализации Compose и его компилятора. Дополнительные ресурсы по Compose можно найти здесь.
Источник