Темы в Compose с Material 3

1. Введение

В этой лаборатории вы узнаете о темизации своих приложений в Jetpack Compose с использованием Material Design 3. Вы также узнаете о ключевых строительных блоках Material Design 3: цветовых схемах, типографике и формах, которые помогут вам персонализировать и доступные способы.

Кроме того, вы изучите поддержку динамического оформления тем и различных уровней акцента.

Что вы узнаете

В этой лаборатории вы узнаете:

  • Ключевые аспекты тематики Материала 3
  • Цветовые схемы Material 3 и способы создания тем для вашего приложения
  • Как поддерживать динамические и светлые/темные темы для вашего приложения
  • Типографика и формы для персонализации вашего приложения
  • Компоненты Material 3 и настройка стиля вашего приложения

Что вы построите

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

d15db3dc75a9d00f.png

Начальная точка нашего приложения по умолчанию с базовой темой.

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

1357cdbfaaa67721.png

Конечная точка кодовой лаборатории тем со светлой цветовой темой и светлой динамической темой.

1357cdbfaaa67721.png

Конечная точка кодовой лаборатории тем с темными темами и темными динамическими темами.

Что тебе понадобится

2. Приступаем к настройке

На этом этапе вы загружаете полный код приложения «Ответить», которое вы будете оформлять в этой лаборатории кода.

Получить код

Код этой лаборатории кода можно найти в репозитории android-compose-codelabs на GitHub. Чтобы клонировать его, запустите:

$ git clone https://github.com/googlecodelabs/android-compose-codelabs

Альтернативно вы можете скачать два zip-файла:

Ознакомьтесь с примером приложения

Только что скачанный код содержит код для всех доступных лабораторий Compose. Чтобы завершить эту лабораторию кода, откройте проект ThemingCodelab в Android Studio.

Мы рекомендуем вам начать с кода в основной ветке и следовать кодовой лаборатории шаг за шагом в удобном для вас темпе. В любой момент вы можете запустить любую версию в Android Studio, изменив ветку git проекта.

Изучение стартового кода

Основной код содержит пакет пользовательского интерфейса, который содержит следующие основные пакеты и файлы, с которыми вы будете взаимодействовать:

  • MainActivity.kt — действие точки входа, при котором вы запускаете приложение «Ответить».
  • com.example.reply.ui.theme — этот пакет содержит темы, типографику и цветовые схемы. В этот пакет вы добавите тему Material.
  • com.example.reply.ui.components — содержит пользовательские компоненты приложения, такие как элементы списка, панели приложений и т. д. К этим компонентам вы примените темы.
  • ReplyApp.kt — это наша основная составная функция, с которой будет начинаться дерево пользовательского интерфейса. В этом файле вы примените тему верхнего уровня.

Эта лаборатория кода будет посвящена файлам пакетов ui .

3. Материал 3 Тематика

Jetpack Compose предлагает реализацию Material Design — комплексной системы проектирования для создания цифровых интерфейсов. Компоненты Material Design (кнопки, карточки, переключатели и т. д.) созданы на основе Material Theming , который представляет собой систематический способ настройки Material Design для лучшего отражения бренда вашего продукта.

Тема Material 3 включает в себя следующие подсистемы для добавления тем в ваше приложение: цветовая схема , типографика и фигуры . Когда вы настраиваете эти значения, ваши изменения автоматически отражаются в компонентах M3, которые вы используете для создания своего приложения. Давайте углубимся в каждую подсистему и реализуем ее в примере приложения.

Подсистемы материального дизайна: цвет, типографика и формы.

Материал 3 подсистема цветов, типографики и форм.

4. Цветовые схемы

Основой цветовой схемы является набор из пяти ключевых цветов, каждый из которых относится к тоновой палитре из 13 тонов, используемых компонентами Материала 3.

Пять базовых ключевых цветов для создания темы M3.

Пять базовых ключевых цветов для создания темы M3.

Каждый акцентный цвет (основной, вторичный и третичный) затем предоставляется в четырех совместимых цветах разных тонов для сочетания, определения акцента и визуального выражения.

Четыре тональных цвета первичных, вторичных и третичных базовых акцентных цветов.

Четыре тональных цвета первичных, вторичных и третичных базовых акцентных цветов.

Точно так же нейтральные цвета также делятся на четыре совместимых тона, используемых для поверхностей и фона. Они также важны для выделения текстовых значков при их размещении на любой поверхности.

Четыре тональных цвета базовых нейтральных цветов.

Четыре тональных цвета базовых нейтральных цветов.

Подробнее о Цветовой схеме и цветовых ролях читайте здесь.

Генерация цветовых схем

Хотя вы можете создать собственную ColorScheme вручную, зачастую проще создать ее, используя исходные цвета вашего бренда. Инструмент Material Theme Builder позволяет вам сделать это и при необходимости экспортировать код темы Compose.

Вы можете выбрать любой цвет, который вам нравится, но в нашем случае вы будете использовать основной цвет ответа по умолчанию #825500 . Нажмите «Основной цвет» в разделе «Основные цвета» слева и добавьте код в палитру цветов.

294f73fc9d2a570e.png

Добавление основного цветового кода в Material Theme Builder.

После того, как вы добавите основной цвет в Конструкторе тем материалов, вы должны увидеть следующую тему и возможность экспорта в правом верхнем углу. Для этой лаборатории вы экспортируете тему в Jetpack Compose.

Конструктор тем материалов с возможностью экспорта в правом верхнем углу.

Конструктор тем материалов с возможностью экспорта в правом верхнем углу.

Основной цвет #825500 создает следующую тему, которую вы добавите в приложение. Материал 3 предоставляет широкий спектр цветовых ролей для гибкого выражения состояния, значимости и акцента компонента.

Экспортирована цветовая схема «Светлая» и «Темная» из основного цвета.

Экспортирована светлая и темная цветовая схема из основного цвета.

Созданный файл The Color.kt содержит цвета вашей темы со всеми ролями, определенными как для светлых, так и для темных цветов темы.

Цвет.кт

package com.example.reply.ui.theme
import androidx.compose.ui.graphics.Color

val md_theme_light_primary = Color(0xFF825500)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFDDB3)
val md_theme_light_onPrimaryContainer = Color(0xFF291800)
val md_theme_light_secondary = Color(0xFF6F5B40)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFFBDEBC)
val md_theme_light_onSecondaryContainer = Color(0xFF271904)
val md_theme_light_tertiary = Color(0xFF51643F)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFD4EABB)
val md_theme_light_onTertiaryContainer = Color(0xFF102004)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1F1B16)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1F1B16)
val md_theme_light_surfaceVariant = Color(0xFFF0E0CF)
val md_theme_light_onSurfaceVariant = Color(0xFF4F4539)
val md_theme_light_outline = Color(0xFF817567)
val md_theme_light_inverseOnSurface = Color(0xFFF9EFE7)
val md_theme_light_inverseSurface = Color(0xFF34302A)
val md_theme_light_inversePrimary = Color(0xFFFFB951)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF825500)
val md_theme_light_outlineVariant = Color(0xFFD3C4B4)
val md_theme_light_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFFFFB951)
val md_theme_dark_onPrimary = Color(0xFF452B00)
val md_theme_dark_primaryContainer = Color(0xFF633F00)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDDB3)
val md_theme_dark_secondary = Color(0xFFDDC2A1)
val md_theme_dark_onSecondary = Color(0xFF3E2D16)
val md_theme_dark_secondaryContainer = Color(0xFF56442A)
val md_theme_dark_onSecondaryContainer = Color(0xFFFBDEBC)
val md_theme_dark_tertiary = Color(0xFFB8CEA1)
val md_theme_dark_onTertiary = Color(0xFF243515)
val md_theme_dark_tertiaryContainer = Color(0xFF3A4C2A)
val md_theme_dark_onTertiaryContainer = Color(0xFFD4EABB)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1F1B16)
val md_theme_dark_onBackground = Color(0xFFEAE1D9)
val md_theme_dark_surface = Color(0xFF1F1B16)
val md_theme_dark_onSurface = Color(0xFFEAE1D9)
val md_theme_dark_surfaceVariant = Color(0xFF4F4539)
val md_theme_dark_onSurfaceVariant = Color(0xFFD3C4B4)
val md_theme_dark_outline = Color(0xFF9C8F80)
val md_theme_dark_inverseOnSurface = Color(0xFF1F1B16)
val md_theme_dark_inverseSurface = Color(0xFFEAE1D9)
val md_theme_dark_inversePrimary = Color(0xFF825500)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFFFB951)
val md_theme_dark_outlineVariant = Color(0xFF4F4539)
val md_theme_dark_scrim = Color(0xFF000000)


val seed = Color(0xFF825500)

Созданный файл The Theme.kt содержит настройки светлых и темных цветовых схем, а также тему приложения. Он также содержит основную функцию компоновки тем AppTheme() .

Тема.кт

package com.example.reply.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable


private val LightColors = lightColorScheme(
   primary = md_theme_light_primary,
   onPrimary = md_theme_light_onPrimary,
   primaryContainer = md_theme_light_primaryContainer,
   onPrimaryContainer = md_theme_light_onPrimaryContainer,
   secondary = md_theme_light_secondary,
   onSecondary = md_theme_light_onSecondary,
   secondaryContainer = md_theme_light_secondaryContainer,
   onSecondaryContainer = md_theme_light_onSecondaryContainer,
   tertiary = md_theme_light_tertiary,
   onTertiary = md_theme_light_onTertiary,
   tertiaryContainer = md_theme_light_tertiaryContainer,
   onTertiaryContainer = md_theme_light_onTertiaryContainer,
   error = md_theme_light_error,
   errorContainer = md_theme_light_errorContainer,
   onError = md_theme_light_onError,
   onErrorContainer = md_theme_light_onErrorContainer,
   background = md_theme_light_background,
   onBackground = md_theme_light_onBackground,
   surface = md_theme_light_surface,
   onSurface = md_theme_light_onSurface,
   surfaceVariant = md_theme_light_surfaceVariant,
   onSurfaceVariant = md_theme_light_onSurfaceVariant,
   outline = md_theme_light_outline,
   inverseOnSurface = md_theme_light_inverseOnSurface,
   inverseSurface = md_theme_light_inverseSurface,
   inversePrimary = md_theme_light_inversePrimary,
   surfaceTint = md_theme_light_surfaceTint,
   outlineVariant = md_theme_light_outlineVariant,
   scrim = md_theme_light_scrim,
)


private val DarkColors = darkColorScheme(
   primary = md_theme_dark_primary,
   onPrimary = md_theme_dark_onPrimary,
   primaryContainer = md_theme_dark_primaryContainer,
   onPrimaryContainer = md_theme_dark_onPrimaryContainer,
   secondary = md_theme_dark_secondary,
   onSecondary = md_theme_dark_onSecondary,
   secondaryContainer = md_theme_dark_secondaryContainer,
   onSecondaryContainer = md_theme_dark_onSecondaryContainer,
   tertiary = md_theme_dark_tertiary,
   onTertiary = md_theme_dark_onTertiary,
   tertiaryContainer = md_theme_dark_tertiaryContainer,
   onTertiaryContainer = md_theme_dark_onTertiaryContainer,
   error = md_theme_dark_error,
   errorContainer = md_theme_dark_errorContainer,
   onError = md_theme_dark_onError,
   onErrorContainer = md_theme_dark_onErrorContainer,
   background = md_theme_dark_background,
   onBackground = md_theme_dark_onBackground,
   surface = md_theme_dark_surface,
   onSurface = md_theme_dark_onSurface,
   surfaceVariant = md_theme_dark_surfaceVariant,
   onSurfaceVariant = md_theme_dark_onSurfaceVariant,
   outline = md_theme_dark_outline,
   inverseOnSurface = md_theme_dark_inverseOnSurface,
   inverseSurface = md_theme_dark_inverseSurface,
   inversePrimary = md_theme_dark_inversePrimary,
   surfaceTint = md_theme_dark_surfaceTint,
   outlineVariant = md_theme_dark_outlineVariant,
   scrim = md_theme_dark_scrim,
)

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

Основным элементом реализации тем в Jetpack Compose является компонуемый MaterialTheme .

Вы помещаете компонуемый MaterialTheme() в функцию AppTheme() , которая принимает два параметра:

  • useDarkTheme — этот параметр привязан к функции isSystemInDarkTheme() для наблюдения за настройками системной темы и применения светлой или темной темы. Если вы хотите вручную сохранить в своем приложении светлую или темную тему, вы можете передать логическое значение в useDarkTheme .
  • content — контент, к которому будет применена тема.

Тема.кт

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

Если вы попытаетесь запустить приложение сейчас, вы увидите, что оно выглядит так же. Несмотря на то, что вы импортировали нашу новую цветовую схему с новыми цветами темы, вы по-прежнему видите базовую тему, поскольку вы не применили тему к приложению «Создать».

Приложение с базовой темой, когда тема не применяется.

Приложение с базовой темой, когда тема не применяется.

Чтобы применить новую тему, в MainActivity.kt оберните основной компонуемый ReplyApp основной функцией тем AppTheme() .

MainActivity.kt

setContent {
   val uiState by viewModel.uiState.collectAsStateWithLifecycle()

   AppTheme {
       ReplyApp(/*..*/)
   }
}

Вы также обновите функции предварительного просмотра, чтобы увидеть, как тема применяется к предварительному просмотру приложений. Оберните составной элемент ReplyApp внутри ReplyAppPreview() с помощью AppTheme , чтобы применить темы к предварительным просмотрам.

В параметрах предварительного просмотра определены как светлая, так и темная системные темы, поэтому вы увидите оба предварительного просмотра.

MainActivity.kt

@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_YES,
   name = "DefaultPreviewDark"
)
@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_NO,
   name = "DefaultPreviewLight"
)
@Composable
fun ReplyAppPreview() {
   AppTheme {
       ReplyApp(
           replyHomeUIState = ReplyHomeUIState(
               emails = LocalEmailsDataProvider.allEmails
           )
       )
   }
}

Если вы запустите приложение сейчас, вы должны увидеть предварительный просмотр приложения с цветами импортированной темы вместо базовой темы.

fddf7b9cc99b1fe3.pngbe7a661b4553167b.png

Приложение с базовой темой (слева).

Приложение с импортированной цветовой темой (справа).

674cec6cc12db6a0.png

Предварительный просмотр светлых и темных приложений с импортированными цветовыми темами.

Материал 3 поддерживает как светлые, так и темные цветовые схемы. Вы только обернули приложение импортированной темой; Компоненты Материала 3 используют цветовые роли по умолчанию.

Давайте узнаем о цветовых ролях и их использовании, прежде чем добавлять их в приложение.

Цветовые роли и доступность

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

1f184a05ea57aa84.png

Цветовые роли первичных, вторичных и третичных цветов.

Первичный — это базовый цвет, который используется для основных компонентов, таких как заметные кнопки и активные состояния.

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

Третичный ключевой цвет используется для создания контрастных акцентов, а нейтральные цвета используются для фона и поверхностей в приложении.

Цветовая система материала обеспечивает стандартные значения тонов и измерения, которые можно использовать для достижения доступных коэффициентов контрастности. Используйте on-primary поверх основного, on-primary-container поверх основного-контейнера и то же самое для других акцентных и нейтральных цветов, чтобы обеспечить пользователю доступный контраст.

Дополнительные сведения см. в разделе Цветовые роли и доступность .

Тональные и теневые возвышения

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

Тональное возвышение с возвышением тени Повышение тона на уровне 2, при котором цвет берется из слота основного цвета.

Наложения возвышений в темных темах также изменились на наложения тональных цветов в Material Design 3. Цвет наложения берется из слота основного цвета.

Поверхность M3 — составная основа большинства компонентов M3 — включает поддержку как тонального, так и теневого возвышения:

Surface(
   modifier = modifier,
   tonalElevation = {..}
   shadowElevation = {..}
) {
   Column(content = content)
}

Добавляем цвета в приложение

Если вы запустите приложение, вы увидите экспортированные цвета, отображаемые в приложении, где компоненты принимают цвета по умолчанию. Теперь, когда мы знаем о цветовых ролях и их использовании, давайте зададим в приложении правильные цветовые роли.

be7a661b4553167b.png

Приложение с цветовой темой и компонентами, принимающими цветовые роли по умолчанию.

Цвета поверхности

На главном экране вы начнете с обертывания основного приложения, которое можно компоновать в Surface() , чтобы обеспечить основу для размещения содержимого приложения поверх него. Откройте MainActivity.kt и оберните ReplyApp() составляемый Surface .

Вы также зададите тональную высоту 5.dp, чтобы придать поверхности тональный цвет основного слота, что поможет обеспечить контраст с элементом списка и панелью поиска над ним. По умолчанию высота тона и тени для поверхности равна 0.dp.

MainActivity.kt

AppTheme {
   Surface(tonalElevation = 5.dp) {
       ReplyApp(
           replyHomeUIState = uiState,
          // other parameters
         )
   }
}

Если вы запустите приложение сейчас и увидите страницу «Список» и «Подробности», вы должны увидеть тоновую поверхность, примененную ко всему приложению.

be7a661b4553167b.pnge70d762495173610.png

Фон приложения без цвета поверхности и тона (слева).

Фон приложения с нанесенным цветом поверхности и тона (справа).

Цвета панели приложений

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

5779fc399d8a8187.png

Пользовательская панель поиска без фона (слева).

Пользовательская панель поиска с фоном (справа).

Теперь вы отредактируете ui/components/ReplyAppBars.kt , который содержит панель приложения. Вы добавите MaterialTheme.colorScheme.background в Modifier Row Composable.

ОтветитьAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

Теперь вы должны увидеть четкое разделение между тональной поверхностью и панелью приложения с цветом фона.

b1b374b801dadc06.png

Панель поиска с цветом фона поверх тональной поверхности.

Цвета плавающих кнопок действий

70ceac87233fe466.png

Большой FAB без какой-либо темы (слева).

Тематический большой FAB с третичным цветом (справа).

На главном экране вы можете улучшить внешний вид плавающей кнопки действия (FAB), чтобы она выделялась как кнопка призыва к действию. Чтобы реализовать это, вы примените к нему третичный акцентный цвет.

В файле ReplyListContent.kt обновите containerColor для FAB на цвет tertiaryContainer , а цвет содержимого на onTertiaryContainer , чтобы сохранить доступность и цветовой контраст.

ReplyListContent.kt

ReplyInboxScreen(/*..*/) {
// Email list content
  LargeFloatingActionButton(
    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
    contentColor = MaterialTheme.colorScheme.onTertiaryContainer
  ){
   /*..*/   
  }
}

Запустите приложение, чтобы увидеть свою тему FAB. Для этой лаборатории кода вы используете LargeFloatingActionButton .

Цвета карт

Список адресов электронной почты на главном экране использует компонент карточки. По умолчанию это заполненная карта , в которой в качестве цвета контейнера используется цвет варианта поверхности, чтобы обеспечить четкое разделение между цветом поверхности и карты. Compose также предоставляет реализации ElevatedCard и OutlinedCard .

Вы можете дополнительно выделить некоторые важные элементы, предоставив дополнительные цветовые тона. Вы измените ui/components/ReplyEmailListItem.kt обновив цвет контейнера карточек с помощью CardDefaults.cardColors() для важных писем:

ОтветEmailListItem.kt

Card(
   modifier =  modifier
       .padding(horizontal = 16.dp, vertical = 4.dp)
       .semantics { selected = isSelected }
       .clickable { navigateToDetail(email.id) },
   colors = CardDefaults.cardColors(
       containerColor = if (email.isImportant)
           MaterialTheme.colorScheme.secondaryContainer
       else MaterialTheme.colorScheme.surfaceVariant
   )
){
  /*..*/   
}

5818200be0b01583.png9367d40023db371d.png

Выделите элемент списка, используя дополнительный цвет контейнера на тональной поверхности.

Цвет элемента подробного списка

Теперь у вас есть тематический главный экран. Взгляните на страницу сведений, щелкнув любой элемент списка адресов электронной почты.

7a9ea7cf3e91e9c7.png79b3874aeca4cd1.png

Страница сведений по умолчанию без элемента тематического списка (слева).

Элемент подробного списка с примененной фоновой темой (справа).

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

ОтветEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background)
           .padding(20.dp)
    ) {
      // List item content
    }
}

Вы можете видеть, что, просто предоставив фон, вы получаете четкое разделение между тональной поверхностью и элементом списка.

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

5. Добавление динамических цветов в приложение

Динамический цвет — ключевая часть Материала 3, в котором алгоритм извлекает пользовательские цвета из обоев пользователя для применения к их приложениям и системному пользовательскому интерфейсу.

Динамическое оформление тем делает ваши приложения более персонализированными. Он также предоставляет пользователям целостный и целостный опыт работы с системной темой.

Динамический цвет доступен на Android 12 и более поздних версиях. Если доступен динамический цвет, вы можете настроить динамическую цветовую схему с помощью dynamicDarkColorScheme() или dynamicLightColorScheme() . Если нет, вам следует вернуться к использованию светлой или темной ColorScheme по умолчанию.

Замените код функции AppTheme в файле Theme.kt на приведенный ниже:

Тема.кт

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
   val context = LocalContext.current
   val colors = when {
       (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> {
           if (useDarkTheme) dynamicDarkColorScheme(context)
           else dynamicLightColorScheme(context)
       }
       useDarkTheme -> DarkColors
       else -> LightColors
   }
   
      MaterialTheme(
       colorScheme = colors,
       content = content
     )
}

fecc63b4c6034236.png

Динамическая тема взята из обоев Android 13.

Запустив приложение сейчас, вы должны увидеть динамические темы, примененные с использованием обоев Android 13 по умолчанию.

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

1095e2b2c1ffdc14.png

Приложение без примененного цвета строки состояния (слева).

Приложение с примененным цветом строки состояния (справа).

Чтобы обновить цвет строки состояния в зависимости от основного цвета вашей темы, добавьте цвет строки состояния после выбора цветовой схемы в составном элементе AppTheme :

Тема.кт

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
 
 // color scheme selection code

 // Add primary status bar color from chosen color scheme.
 val view = LocalView.current
 if (!view.isInEditMode) {
    SideEffect {
        val window = (view.context as Activity).window
        window.statusBarColor = colors.primary.toArgb()
        WindowCompat
            .getInsetsController(window, view)
            .isAppearanceLightStatusBars = useDarkTheme
    }
 }
   
  MaterialTheme(
    colorScheme = colors,
     content = content
   )
}

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

69093b5bce31fd43.png

Динамическая светлая (слева) и темная (справа) тема, примененная к обоям Android 13 по умолчанию.

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

6. Типографика

Material Design 3 определяет масштаб типа . Именование и группировка были упрощены: отображение, заголовок, заголовок, тело и метка с большими, средними и маленькими размерами для каждого.

999a161dcd9b0ec4.png

Материал 3 типа шкалы.

Определение типографики

Compose предоставляет класс M3 Typography , а также существующие классы TextStyle и классы font-related , для моделирования масштаба шрифта Material 3.

Конструктор Typography предлагает значения по умолчанию для каждого стиля, поэтому вы можете опустить любые параметры, которые не хотите настраивать. Дополнительные сведения см. в разделе Стили оформления и их значения по умолчанию .

В вашем приложении вы будете использовать пять стилей оформления: headlineSmall , titleLarge , bodyLarge , bodyMedium и labelMedium . Эти стили будут охватывать как главный экран, так и экран подробностей.

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

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

Затем перейдите к пакету ui/theme и откройте Type.kt Добавьте следующий код, чтобы обеспечить собственную реализацию некоторых стилей текста вместо значений по умолчанию:

Тип.кт

val typography = Typography(
   headlineSmall = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 24.sp,
       lineHeight = 32.sp,
       letterSpacing = 0.sp
   ),
   titleLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 18.sp,
       lineHeight = 28.sp,
       letterSpacing = 0.sp
   ),
   bodyLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 16.sp,
       lineHeight = 24.sp,
       letterSpacing = 0.15.sp
   ),
   bodyMedium = TextStyle(
       fontWeight = FontWeight.Medium,
       fontSize = 14.sp,
       lineHeight = 20.sp,
       letterSpacing = 0.25.sp
   ),
   labelMedium = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 12.sp,
       lineHeight = 16.sp,
       letterSpacing = 0.5.sp
   )
)

Ваша типографика теперь определена. Чтобы добавить его в свою тему, передайте его в компонуемый MaterialTheme() внутри AppTheme :

Тема.кт

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       content = content
   )
}

Работа с типографикой

Как и в случае с цветами, вы получите доступ к стилю оформления текущей темы с помощью MaterialTheme.typography . Это дает вам экземпляр типографии, позволяющий использовать всю типографику, определенную в Type.k t.

Text(
   text = "Hello M3 theming",
   style = MaterialTheme.typography.titleLarge
)

Text(
   text = "you are learning typography",
   style = MaterialTheme.typography.bodyMedium
)

Вашему продукту, скорее всего, не понадобятся все 15 стилей по умолчанию из шкалы типов Material Design. В этой кодовой лаборатории выбрано пять размеров, а остальные опущены.

Поскольку вы не применили типографику к составным объектам Text( ), весь текст по умолчанию возвращается к Typography.bodyLarge .

Типографика домашнего списка

Затем примените типографику к функции ReplyEmailListItem в ui/components/ReplyEmailListItem.kt чтобы создать различие между заголовками и метками:

ОтветEmailListItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.titleLarge,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   overflow = TextOverflow.Ellipsis
)

90645c0765167bb7.png6c4af2f412c18bfb.png

Главный экран без типографики (слева).

Главный экран с нанесенной типографикой (справа).

Типографика подробного списка

Аналогичным образом вы добавите типографику на экран подробностей, обновив все текстовые элементы ReplyEmailThreadItem в u i/components/ReplyEmailThreadItem.kt :

ОтветEmailThreadItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = stringResource(id = R.string.twenty_mins_ago),
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.bodyMedium,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

543ac09e43d8761.png3412771e95a45f36.png

Подробный экран без применения типографики (слева).

Подробный экран с нанесенной типографикой (справа).

Настройка типографики

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

Вы измените стиль текста в файле theme/Type.kt , который будет отражен во всех использующих его компонентах.

Обновите fontWeight до SemiBold и lineHeight до 32.sp для titleLarge , который используется для темы в элементе списка. Это сделает больший акцент на предмете и обеспечит четкое разделение.

Тип.кт

...
titleLarge = TextStyle(
   fontWeight = FontWeight.SemiBold,
   fontSize = 18.sp,
   lineHeight = 32.sp,
   letterSpacing = 0.0.sp
),
...

f8d2212819eb0b61.png

Применение пользовательской типографики к тексту темы.

7. Формы

Поверхности материала могут отображаться в различных формах. Формы направляют внимание, определяют компоненты, сообщают о состоянии и выражают бренд.

Определение фигур

Compose предоставляет классу Shapes расширенные параметры для реализации новых фигур M3. Масштаб фигур M3, аналогичный масштабу текста , обеспечивает выразительный диапазон фигур в пользовательском интерфейсе.

В шкале фигур имеются разные размеры фигур:

  • Очень маленький
  • Маленький
  • Середина
  • Большой
  • Очень большой

По умолчанию каждая фигура имеет значение по умолчанию, которое можно переопределить. В своем приложении вы будете использовать среднюю фигуру для изменения элемента списка, но вы также можете объявить и другие фигуры. Создайте новый файл Shape.kt в пакете ui/theme и добавьте код для фигур:

Форма.кт

package com.example.reply.ui.theme

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp

val shapes = Shapes(
   extraSmall = RoundedCornerShape(4.dp),
   small = RoundedCornerShape(8.dp),
   medium = RoundedCornerShape(16.dp),
   large = RoundedCornerShape(24.dp),
   extraLarge = RoundedCornerShape(32.dp)
)

Теперь, когда вы определили свои shapes , передайте их в M3 MaterialTheme , как вы это делали для цветов и типографики:

Тема.кт

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       shapes = shapes
       content = content
   )
}

Работа с фигурами

Точно так же, как цвет и типографику, вы можете применять формы к компонентам Material с помощью MaterialTheme.shape , который предоставляет вам экземпляр Shape для доступа к формам Material.

Ко многим компонентам Material уже применены формы по умолчанию, но вы можете предоставить и применить к компонентам свои собственные формы через доступные слоты.

Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(shape = MaterialTheme.shapes.large) { /* fab content */}

Значения форм по умолчанию для всех компонентов Материала 3. Сопоставление компонентов материала с использованием различных типов форм.

Посмотреть отображение фигур для всех компонентов можно в документации Shape.

Доступны еще две фигуры — RectangleShape и CircleShape , которые являются частью Compose. Прямоугольная форма не имеет радиуса границы, а форма круга имеет полностью закругленные края.

Вы также можете применить форму к своим компонентам, используя Modifiers , принимающие формы, такие как Modifier.clip , Modifier.background и Modifier.border .

Форма панели приложений

Мы хотим, чтобы панель приложения имела закругленный угол фона:

f873392abe535494.png

TopAppBar использует Row с цветом фона. Чтобы добиться закругленного угла фона, определите форму фона, передав CircleShape модификатору фона:

ОтветитьAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(
               MaterialTheme.colorScheme.background,
               CircleShape
           ),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

f873392abe535494.png

Форма элемента подробного списка

На главном экране вы используете карточку, которая по умолчанию использует Shape.Medium . Однако для нашей страницы сведений вместо этого вы использовали столбец с цветом фона. Для однородного вида списка примените к нему среднюю фигуру.

3412771e95a45f36.png80ee881c41a98c2a.png

Столбец элемента подробного списка без формы элемента списка (слева) и средней формы в списке (справа).

ОтветEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(8.dp)
           .background(
               MaterialTheme.colorScheme.background,
               MaterialTheme.shapes.medium
           )
           .padding(16.dp)

   ) {
      // List item content
      
   }
}

Теперь при запуске вашего приложения вы увидите подробный элемент списка экрана medium размера.

8. Акцент

Акцент в пользовательском интерфейсе помогает вам выделить один контент над другим, например, когда вы хотите отличить заголовок от субтитров. Акцент в M3 использует вариации цвета и его цветовые комбинации. У вас есть два способа добавить акцент:

  1. Использование поверхности, варианта поверхности и фона наряду с цветами поверхности и вариантов поверхности из расширенной цветовой системы M3.

Например, поверхность можно использовать с вариантом на поверхности, а вариант поверхности можно использовать с вариантом на поверхности, чтобы обеспечить разные уровни акцента.

Варианты поверхности также можно использовать с акцентными цветами, чтобы обеспечить меньший акцент, чем с акцентными цветами, но при этом оставаться доступными и соответствовать коэффициенту контрастности.

Цветовые роли поверхности, фона и варианта поверхности.

Цветовые роли поверхности, фона и варианта поверхности.

  1. Использование разной толщины шрифта для текста. Как вы видели в разделе «Типографика», вы можете задать собственный вес для шкалы шрифта, чтобы придать различный акцент.

Затем обновите ReplyEmailListItem.kt , чтобы обеспечить разницу акцентов, используя поверхностный вариант. По умолчанию содержимое карточки имеет цвет по умолчанию в зависимости от фона.

Вы обновите цвет составного текста времени и основного текста на onSurfaceVariant . Это уменьшает его акцент по сравнению с onContainerColors , который по умолчанию применяется к компонуемым текстам темы и заголовка.

2c9b7f2bd016edb8.png6850ff391f21e4ba.png

Время и основной текст с тем же акцентом, что и тема и заголовок (слева).

Время и тело с меньшим акцентом по сравнению с предметом и названием (справа).

ОтветEmailListItem.kt

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant
   overflow = TextOverflow.Ellipsis
)

Для важной карточки электронной почты с фоновым secondaryContainer весь цвет текста по умолчанию является цветом onSecondaryContainer . Для остальных писем фоном является surfaceVariant, поэтому для всего текста по умолчанию используется цвет onSurfaceVariant .

9. Поздравления

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

2d8fcabf15ac5202.png5a4d31db0185dca6.pngce009e4ce560834d.png

Конец темирования приводит к применению динамических цветов и цветовой темы.

Что дальше

Ознакомьтесь с другими нашими лабораториями по написанию кода :

дальнейшее чтение

Примеры приложений

Справочная документация