UI Components
https://developer.android.com/develop/ui/compose/layouts/flow |
https://developer.android.com/develop/ui/compose/layouts/pager |
https://developer.android.com/develop/ui/compose/layouts/constraintlayout |
Components
Text
Отображает текст на экране.
Text(text = "Hello, Compose!")
Текст фиксированной высоты c расположением center_vertical
Text(
text = stringResource(R.string.text),
modifier = Modifier
.constrainAs(anyText) {
width = Dimension.wrapContent
height = Dimension.value(48.dp)
start.linkTo(parent.start)
top.linkTo(parent.top)
}
.wrapContentHeight(Alignment.CenterVertically)
)
Enforcing constraints. Текст сокращается в зависимости от контента рядом
Row {
Text(
text = "Text",
overflow = TextOverflow.Ellipsis,
maxLines = 1,
modifier = Modifier.weight(weight = 1F, fill = false)
)
Icon(
painter = painterResource(R.drawable.ic_icon),
contentDescription = null
)
}
BasicText
Базовый компонент для отображения текста.
BasicText("Hello, World!")
ClickableText
Текст, который реагирует на клики.
ClickableText(
text = AnnotatedString("Click me"),
onClick = { offset -> /* handle click */ }
)
Button
Кнопка, которая выполняет действие при нажатии.
Button(
onClick = { /* Handle click */ }
) {
Text(text = "Button")
}
Preview
TextButton
Кнопка с текстом, без фона и границ.
TextButton(
onClick = { /* Handle click */ }
) {
Text(text = "Text Button")
}
Preview
OutlinedButton
Кнопка с контуром вместо заполнения.
OutlinedButton(
onClick = { /* Handle click */ }
) {
Text(text = "Outlined Button")
}
Preview
IconButton
Кнопка, которая содержит только иконку.
IconButton(
onClick = { /* Handle click */ }
) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = "Favorite"
)
}
Preview
FilledIconButton
Кнопка с иконкой и заполненным фоном.
FilledIconButton(
onClick = { /* Handle click */ }
) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Favorite"
)
}
Preview
FilledTonalIconButton
Кнопка с иконкой и менее ярким фоном.
FilledTonalIconButton(
onClick = { /* Handle click */ }
) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Favorite"
)
}
Preview
OutlinedIconButton
Кнопка с иконкой и обводкой.
OutlinedIconButton(
onClick = { /* Handle click */ }
) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Favorite"
)
}
Preview
ElevatedButton
Кнопка с тенью.
ElevatedButton(
onClick = { /* Handle click */ }
) {
Text(text = "Elevated Button")
}
Preview
FilledTonalButton
Кнопка с заполненным, но менее ярким фоном.
FilledTonalButton(
onClick = { /* Handle click */ }
) {
Text(text = "Filled Tonal Button")
}
Preview
IconToggleButton
Кнопка-переключатель с иконкой.
var isChecked by remember { mutableStateOf(false) }
IconToggleButton(
checked = isChecked,
onCheckedChange = { isChecked = it }
) {
val icon = if (isChecked) Icons.Default.Favorite else Icons.Default.FavoriteBorder
val tint = if (isChecked) Color.Red else Color.Gray
Icon(
imageVector = icon,
contentDescription = if (isChecked) "Удалить из избранного" else "Добавить в избранное",
tint = tint
)
}
Preview
FilledIconToggleButton
Кнопка-переключатель с заполненным фоном.
var isChecked by remember { mutableStateOf(false) }
FilledIconToggleButton(
checked = isChecked,
onCheckedChange = { isChecked = it }
) {
val icon = if (isChecked) Icons.Default.Star else Icons.Default.StarBorder
val tint = if (isChecked) Color.Yellow else Color.Gray
Icon(
imageVector = icon,
contentDescription = if (isChecked) "Удалить из избранного" else "Добавить в избранное",
tint = tint
)
}
Preview
FilledTonalIconToggleButton
Кнопка-переключатель с менее ярким фоном.
var isChecked by remember { mutableStateOf(false) }
FilledTonalIconToggleButton(
checked = isChecked,
onCheckedChange = { isChecked = it }
) {
val icon = if (isChecked) Icons.Default.Star else Icons.Default.StarBorder
val tint = if (isChecked) Color.Yellow else Color.Gray
Icon(
imageVector = icon,
contentDescription = if (isChecked) "Удалить из избранного" else "Добавить в избранное",
tint = tint
)
}
Preview
OutlinedIconToggleButton
Кнопка-переключатель с обводкой.
var isChecked by remember { mutableStateOf(false) }
OutlinedIconToggleButton(
checked = isChecked,
onCheckedChange = { isChecked = it }
) {
val icon = if (isChecked) Icons.Default.Star else Icons.Default.StarBorder
val tint = if (isChecked) Color.Yellow else Color.Gray
Icon(
imageVector = icon,
contentDescription = if (isChecked) "Удалить из избранного" else "Добавить в избранное",
tint = tint
)
}
Preview
Image
Отображает изображение.
Image(
painter = painterResource(id = R.drawable.image),
contentDescription = "Example Image"
)
Icon
Отображает иконку, например из набора Material Icons.
Icon(imageVector = Icons.Default.Home, contentDescription = "Home Icon")
TextField
Поле для ввода текста.
var text by remember { mutableStateOf("Text") }
TextField(
value = text,
onValueChange = { newText -> text = newText },
modifier = Modifier.fillMaxWidth(0.8F),
textStyle = TextStyle(color = Color.Black, fontSize = 16.sp),
label = { Text(text = "Введите текст") },
placeholder = { Text(text = "Текст") }
)
Preview
Floating Suffix
class SuffixTransformation(
private val suffix: String
): VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val result = text + AnnotatedString(
text = suffix
)
val textWithSuffixMapping = object: OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return offset
}
override fun transformedToOriginal(offset: Int): Int {
if (text.isEmpty()) return 0
if (offset >= text.length) return text.length
return offset
}
}
return TransformedText(result, textWithSuffixMapping )
}
}
TextField(
value = text,
onValueChange = { value: String ->
text = value
},
visualTransformation = if (text.isNotEmpty()) SuffixTransformation(suffix = "₽") else VisualTransformation.None
)
Password Field
var password by remember { mutableStateOf("") }
var passwordVisible by rememberSaveable { mutableStateOf(false) }
TextField(
value = titleText,
onValueChange = { value: String ->
password = value
},
trailingIcon = {
AnimatedVisibility(
visible = password.isNotEmpty()
) {
IconButton(
onClick = {
passwordVisible = !passwordVisible
}
) {
Icon(
imageVector = if (passwordVisible) MoviesIcons.Visibility else MoviesIcons.VisibilityOff,
contentDescription = null
)
}
}
},
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation()
)
BasicTextField
Базовый компонент для ввода текста в Compose, предоставляющий большую гибкость и настраиваемость по сравнению с TextField
. Не содержит встроенного оформления и анимаций, таких как подсказки, рамки или иконки, что позволяет разработчику полностью контролировать его внешний вид и поведение. BasicTextField
подходит для создания кастомных полей ввода, где внешний вид и взаимодействие требуют более тонкой настройки.
var text by remember { mutableStateOf("Text") }
BasicTextField(
value = text,
onValueChange = { newText -> text = newText },
modifier = Modifier.fillMaxWidth(0.8F),
textStyle = TextStyle(color = Color.Black, fontSize = 36.sp),
cursorBrush = SolidColor(Color.Blue)
)
Preview
OutlinedTextField
Текстовое поле с обводкой.
var text by remember { mutableStateOf("Text") }
OutlinedTextField(
value = text,
onValueChange = { newText -> text = newText },
modifier = Modifier.fillMaxWidth(0.8F),
textStyle = TextStyle(color = Color.Black, fontSize = 16.sp),
label = { Text(text = "Введите текст") },
placeholder = { Text(text = "Текст") }
)
Preview
Checkbox
Компонент для выбора «включено/выключено».
var checked by remember { mutableStateOf(false) }
Checkbox(
checked = checked,
onCheckedChange = { checked = it }
)
Switch
Переключатель для изменения состояния (аналог чекбокса, но в стиле тумблера).
var switched by remember { mutableStateOf(false) }
Switch(
checked = switched,
onCheckedChange = { switched = it }
)
Slider
Ползунок для выбора значения.
var sliderValue by remember { mutableStateOf(0f) }
Slider(
value = sliderValue,
onValueChange = { sliderValue = it }
)
RangeSlider
Ползунок с диапазоном значений.
var range by remember { mutableStateOf(0f..100f) }
RangeSlider(values = range, onValueChange = { range = it })
InlineSlider
Ползунок для выбора значений.
InlineSlider(value = 0.5f, onValueChange = { /* Handle change */ })
RadioButton
Радио-кнопка для выбора одного элемента из группы.
var selected by remember { mutableStateOf(false) }
RadioButton(
selected = selected,
onClick = { selected = !selected }
)
RadioGroup
Группа радиокнопок для выбора одного варианта.
Column {
RadioButton(selected = true, onClick = { /* Действие */ })
RadioButton(selected = false, onClick = { /* Действие */ })
}
Divider
Линия-разделитель между элементами.
Divider()
Spacer
Невидимый элемент, используемый для создания отступов между элементами.
Spacer(modifier = Modifier.height(16.dp))
CircularProgressIndicator
Индикатор выполнения в виде круга.
CircularProgressIndicator()
LinearProgressIndicator
Индикатор выполнения в виде полосы.
LinearProgressIndicator()
Card
Карточка с закругленными углами.
Card(elevation = 4.dp) {
Text("This is a card")
}
ElevatedCard
Карточка с тенью.
ElevatedCard {
Text("Elevated Card")
}
OutlinedCard
Карточка с обводкой.
OutlinedCard {
Text("Outlined Card")
}
AppCard
Карточка для отображения информации.
AppCard {
Text("App Card")
}
TitleCard
Карточка с заголовком.
TitleCard {
Text("Title Card")
}
ClassicCard
Классическая карточка.
ClassicCard {
Text("Classic Card")
}
WideClassicCard
Широкая классическая карточка.
WideClassicCard {
Text("Wide Classic Card")
}
CompactCard
Компактная карточка.
CompactCard {
Text("Compact Card")
}
SwipeToRevealCard
Карточка с функцией свайпа для раскрытия.
SwipeToRevealCard(
swipeThreshold = 100.dp,
onSwipe = { /* Handle swipe action */ }
) {
Text("Swipe Me")
}
FloatingActionButton
Плавающая кнопка для важных действий.
FloatingActionButton(onClick = { /* Действие */ }) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
SmallFloatingActionButton
Небольшая плавающая кнопка действия.
SmallFloatingActionButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Add, contentDescription = null)
}
LargeFloatingActionButton
Большая плавающая кнопка действия.
LargeFloatingActionButton(onClick = { /* Handle click */ }) {
Icon(Icons.Filled.Add, contentDescription = null)
}
ExtendedFloatingActionButton
Расширенная версия FloatingActionButton
с текстом.
ExtendedFloatingActionButton(
text = { Text("Action") },
onClick = { /* Handle click */ }
)
TopAppBar
Компонент для верхней панели приложения.
TopAppBar(title = { Text("App Title") })
BottomAppBar
Нижняя панель приложения, часто используется с FAB.
BottomAppBar {
Text("Bottom App Bar")
}
BottomNavigation
Нижняя панель для навигации по приложению.
BottomNavigation {
BottomNavigationItem(icon = { Icon(Icons.Default.Home, contentDescription = null) }, selected = false, onClick = { /* Действие */ })
}
NavigationDrawer
Панель навигации сбоку, используемая для отображения меню.
ModalDrawer(
drawerContent = { Text("Drawer content") },
content = { Text("Main content") }
)
TabRow
Компонент для создания вкладок.
var selectedTabIndex by remember { mutableStateOf(0) }
TabRow(selectedTabIndex = selectedTabIndex) {
Tab(selected = selectedTabIndex == 0, onClick = { selectedTabIndex = 0 }) {
Text("Tab 1")
}
Tab(selected = selectedTabIndex == 1, onClick = { selectedTabIndex = 1 }) {
Text("Tab 2")
}
}
ScrollableTabRow
Панель с вкладками, которая поддерживает горизонтальную прокрутку.
ScrollableTabRow(selectedTabIndex = 0) {
Text("Tab 1")
Text("Tab 2")
}
PrimaryTabRow
Панель вкладок без прокрутки.
PrimaryTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
PrimaryScrollableTabRow
Панель вкладок с возможностью прокрутки.
PrimaryScrollableTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
SecondaryTabRow
Дополнительная панель вкладок без прокрутки.
SecondaryTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
SecondaryScrollableTabRow
Дополнительная панель вкладок с прокруткой.
SecondaryScrollableTabRow(selectedTabIndex = 0) {
Tab(selected = true, onClick = { /* Handle click */ }) {
Text("Tab 1")
}
}
Surface
Контейнер для отображения компонентов с поддержкой стилей, таких как elevation и фоны.
Surface(elevation = 4.dp, shape = RoundedCornerShape(8.dp)) {
Text("Surface with elevation")
}
Chip
Элемент для отображения маленьких блоков текста или иконок.
Chip(onClick = { /* Handle click */ }) {
Text("Chip")
}
AssistChip
Чип для дополнительных действий или помощи.
AssistChip(onClick = { /* Handle click */ }) {
Text("Assist Chip")
}
FilterChip
Чип для фильтрации данных.
FilterChip(onClick = { /* Handle click */ }) {
Text("Filter Chip")
}
InputChip
Чип для ввода данных.
InputChip(onClick = { /* Handle click */ }) {
Text("Input Chip")
}
SuggestionChip
Чип для предложений.
SuggestionChip(onClick = { /* Handle click */ }) {
Text("Suggestion Chip")
}
Badge
Отображает небольшой значок или метку.
Badge {
Text("New")
}
Carousel
Компонент для прокрутки элементов в виде карусели.
Carousel(
items = listOf("Item 1", "Item 2", "Item 3"),
content = { item -> Text(item) }
)
DatePicker
Выбор даты.
DatePickerDialog(
onDateSelected = { /* Handle date selection */ }
)
TimePicker
Выбор времени.
TimePickerDialog(
onTimeSelected = { /* Handle time selection */ }
)
NavigationRail
Боковая панель для навигации
NavigationRail {
NavigationRailItem(
icon = { Icon(Icons.Filled.Home, contentDescription = null) },
label = { Text("Home") },
selected = false,
onClick = { /* Handle click */ }
)
}
SearchBar
Панель для поиска.
SearchBar(onSearch = { /* Handle search */ }) {
Text("Search")
}
Layouts
Column
Размещает элементы вертикально.
Column {
Text("Item 1")
Text("Item 2")
}
Row
Размещает элементы горизонтально.
Row {
Text("Item 1")
Text("Item 2")
}
Box
Контейнер, который накладывает элементы друг на друга.
Box {
Text("First item")
Text("Second item")
}
FlowRow
Располагает элементы в строках, которые автоматически переносятся на новую строку при переполнении ширины.
FlowRow {
repeat(10) { index ->
Text("Item $index")
}
}
FlowColumn
Аналог FlowRow
, но с вертикальной ориентацией.
FlowColumn {
repeat(10) { index ->
Text("Item $index")
}
}
ContextualFlowRow
Компонент для размещения элементов в строку с поддержкой контекстного управления.
ContextualFlowRow(
context = /* Provide context */,
modifier = Modifier.padding(8.dp)
) {
items(/* List of items */) { item ->
Text(text = item)
}
}
ContextualFlowColumn
Компонент для размещения элементов в колонку с поддержкой контекстного управления.
ContextualFlowColumn(
context = /* Provide context */,
modifier = Modifier.padding(8.dp)
) {
items(/* List of items */) { item ->
Text(text = item)
}
}
LazyColumn
Отображает длинный спискок элементов с ленивой загрузкой. Он оптимизирован для работы с большими наборами данных, загружая элементы по мере необходимости, что позволяет экономить ресурсы и увеличивать производительность приложения.
LazyColumn {
items(10) { index ->
Text("Item $index")
}
}
item
Добавлztn отдельный элемент в LazyColumn
. Этот метод позволяет отображать один элемент без необходимости оборачивать его в коллекцию.
LazyColumn {
item {
Text(text = "Заголовок")
}
item {
Text(text = "Первый элемент")
}
}
items
Используется для отображения списка элементов. Он принимает коллекцию и автоматически создает элементы списка на основе этой коллекции.
LazyColumn {
items(listOf("Первый", "Второй", "Третий")) { item ->
Text(text = item)
}
}
itemsIndexed
Позволяет получить доступ к индексу каждого элемента в списке. Это полезно, когда нужно отображать индекс элемента или выполнять какие-либо действия на основе его позиции.
LazyColumn {
itemsIndexed(listOf("Первый", "Второй", "Третий")) { index, item ->
Text(text = "$index: $item")
}
}
stickyHeader
Позволяет создать заголовок, который будет оставаться на месте при прокрутке. Это полезно для создания разделов в длинном списке.
LazyColumn {
stickyHeader {
Text(text = "Заголовок раздела", style = MaterialTheme.typography.h6)
}
items(listOf("Элемент 1", "Элемент 2", "Элемент 3")) { item ->
Text(text = item)
}
}
key
Используется для указания уникального ключа для каждого элемента в списке, что позволяет эффективно управлять состоянием и производительностью при обновлениях списка. Использование ключей помогает Compose отслеживать элементы и сохранять их состояние даже при изменении данных. key
используется в методах items
, itemsIndexed
, и item
, позволяя указать уникальный идентификатор для каждого элемента.
• Эффективное обновление: Когда элементы в списке обновляются, Compose может использовать ключи, чтобы определить, какие элементы были изменены, добавлены или удалены. Это позволяет избежать полной переработки всего списка и обновлять только измененные элементы.
• Сохранение состояния: Если элементы списка имеют свое состояние (например, флажки, текстовые поля и т. д.), использование уникальных ключей позволяет сохранять состояние каждого элемента, даже если порядок элементов меняется или элементы удаляются/добавляются.
data class User(val id: String, val name: String)
val users = listOf(
User("1", "Alice"),
User("2", "Bob"),
User("3", "Charlie")
)
LazyColumn {
items(users, key = { it.id }) { user ->
Text(text = user.name)
}
}
LazyRow
Горизонтальный список с возможностью прокрутки.
LazyRow {
items(10) { index ->
Text("Item $index")
}
}
LazyVerticalGrid
Отображает элементы в виде сетки с возможностью прокрутки.
LazyVerticalGrid(cells = GridCells.Fixed(2)) {
items(10) { index ->
Text("Item $index")
}
}
Добавить Header
LazyVerticalGrid(
columns = GridCells.Fixed(count = 8),
) {
item(
span = { GridItemSpan(maxLineSpan) }
) {
Text(
text = "Header",
)
}
items(list) { item ->
...
}
}
LazyStaggeredGrid
Прокручиваемая сетка с переменной высотой элементов.
LazyStaggeredGrid(columns = StaggeredGridCells.Fixed(2)) {
items(100) { index ->
Text("Item #$index")
}
}
Scaffold
Структурный компонент для создания макетов с верхними панелями, навигацией, FAB и контентом.
Scaffold(
topBar = { TopAppBar(title = { Text("AppBar Title") }) },
floatingActionButton = { FloatingActionButton(onClick = { /* Действие */ }) { Text("+") } },
content = { Text("Hello, Scaffold!") }
)
BackdropScaffold
Специальный макет, где один элемент (фоновый) может быть скрыт или отображен позади другого контента.
BackdropScaffold(
appBar = { TopAppBar(title = { Text("Backdrop Scaffold") }) },
backLayerContent = { Text("Back Layer") },
frontLayerContent = { Text("Front Layer") }
)
HorizontalPager
Пейджер для горизонтальной прокрутки страниц.
HorizontalPager(count = 5) { page ->
Text("Page $page")
}
VerticalPager
Пейджер для вертикальной прокрутки страниц (как и HorizontalPager
, но с вертикальной ориентацией).
VerticalPager(count = 5) { page ->
Text("Page $page")
}
ConstraintLayout
Chain
ConstraintLayout {
val (icon, text) = createRefs()
createHorizontalChain(icon, text, chainStyle = ChainStyle.Packed)
Icon(
modifier = Modifier
.constrainAs(icon) {
width = Dimension.wrapContent
height = Dimension.wrapContent
start.linkTo(parent.start)
top.linkTo(parent.top)
end.linkTo(text.start)
bottom.linkTo(parent.bottom)
}
)
Text(
modifier = Modifier
.constrainAs(text) {
width = Dimension.wrapContent
height = Dimension.wrapContent
start.linkTo(icon.end)
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
.padding(start = 8.dp)
)
}
Flow
FlowRow {
Text(
text = "Text 1",
modifier = Modifier.padding(end = 8.dp)
)
Text(
text = "Text 2",
modifier = Modifier.padding(end = 8.dp)
)
Text(
text = "Text 3"
)
}
Adaptive Layouts
BoxWithConstraints
Контейнер, который может изменять свою компоновку в зависимости от доступных размеров.
BoxWithConstraints { constraints ->
if (constraints.maxWidth < 600.dp) {
// Компоновка для узкого экрана
Column {
Text("Узкий экран")
// Дополнительные элементы
}
} else {
// Компоновка для широкого экрана
Row {
Text("Широкий экран")
// Дополнительные элементы
}
}
}
NavigationSuiteScaffold
Интегрируется с NavController
для навигации между экранами и поддерживает стандартные элементы интерфейса, такие как TopAppBar
и BottomNavigation
.
val navController = rememberNavController()
NavigationSuiteScaffold(
navController = navController,
topBar = { TopAppBar(title = { Text("MyApp") }) },
bottomBar = {
BottomNavigation {
// Навигационные элементы
}
}
) { innerPadding ->
NavHost(
navController = navController,
startDestination = "home",
Modifier.padding(innerPadding)
) {
composable("home") { HomeScreen(navController) }
composable("details/{itemId}") { backStackEntry ->
DetailsScreen(navController, itemId = backStackEntry.arguments?.getString("itemId"))
}
}
}
ListDetailPaneScaffold
Управляет макетом, разделяя экран на две основные панели: список и детальную информацию.
ListDetailPaneScaffold(
listContent = { innerPadding ->
LazyColumn(
Modifier.padding(innerPadding)
) {
items(items = itemsList) { item ->
ListItem(
modifier = Modifier.clickable { /* Отображаем детальную информацию */ },
text = { Text(item.title) }
)
}
}
},
detailContent = { innerPadding ->
// Отображение детальной информации о выбранном элементе
DetailView(
modifier = Modifier.padding(innerPadding),
item = selectedItem
)
}
)
SupportingPaneScaffold
Создает интерфейсы с двумя панелями — основной и вспомогательной.
SupportingPaneScaffold(
mainContent = { innerPadding ->
// Основной контент
Column(
modifier = Modifier.padding(innerPadding)
) {
Text("Основной контент")
// Дополнительные элементы
}
},
supportingContent = { innerPadding ->
// Вспомогательный контент
Column(
modifier = Modifier.padding(innerPadding)
) {
Text("Вспомогательный контент")
// Дополнительные элементы
}
}
)
NavigableListDetailPaneScaffold
Обеспечивает структуру для приложений с навигацией между списком элементов и детализированным просмотром выбранного элемента.
val navigator = rememberListDetailPaneScaffoldNavigator<Any>()
NavigableListDetailPaneScaffold(
navigator = navigator,
listPane = {}, // панель списка
detailPane = {}, // Панель деталей
)
Dialogs
Popup
Всплывающее окно для отображения временного контента поверх текущего интерфейса.
Popup {
Text("Popup content")
}
DropdownMenu
Выпадающее меню для выбора из нескольких вариантов.
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
DropdownMenuItem(onClick = { /* Действие */ }) {
Text("Option 1")
}
DropdownMenuItem(onClick = { /* Действие */ }) {
Text("Option 2")
}
}
AlertDialog
Диалоговое окно для отображения сообщений и взаимодействия с пользователем.
AlertDialog(
onDismissRequest = { /* Закрыть диалог */ },
title = { Text("Title") },
text = { Text("Dialog content goes here") },
confirmButton = {
Button(onClick = { /* Действие подтверждения */ }) {
Text("OK")
}
}
)
ModalBottomSheet
Модальное нижнее окно, которое появляется снизу экрана и блокирует взаимодействие с остальными элементами, пока не будет закрыто.
@Composable
fun ExampleModalBottomSheet() {
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
Column(modifier = Modifier.padding(16.dp)) {
Text("Title")
Button(onClick = { scope.launch { sheetState.hide() } }) {
Text("Close Bottom Sheet")
}
}
}
) {
Button(onClick = { scope.launch { sheetState.show() } }) {
Text("Open Bottom Sheet")
}
}
}
ModalBottomSheetLayout
Открывает листовое окно снизу для отображения контента.
ModalBottomSheetLayout(
sheetContent = { Text("Bottom Sheet Content") },
content = { Text("Main Content") }
)
TooltipBox
Контейнер для отображения подсказок.
TooltipBox(tooltip = { Text("Tooltip") }) {
Text("Hover me")
}
BasicTooltipBox
Простой контейнер для подсказок.
BasicTooltipBox(
tooltip = { Text("Tooltip") }
) {
Text("Hover me")
}
AnimatedVisibility
Компонент для плавного отображения или скрытия контента.
var visible by remember { mutableStateOf(true) }
AnimatedVisibility(
visible = visible
) {
Text("Animated Content")
}
Crossfade
Компонент для анимации плавной смены контента.
Crossfade(
targetState = selectedScreen
) { screen ->
when (screen) {
Screen.Home -> HomeScreen()
Screen.Profile -> ProfileScreen()
}
}
Placeholder
Контейнер для отображения placeholder-заполнителя (например, для состояния загрузки).
Placeholder(
visible = true,
modifier = Modifier.size(100.dp)
)
AndroidView
Позволяет встроить классический Android View в Compose.
AndroidView(
factory = { context ->
TextView(context).apply { text = "Hello, View!" }
}
)
Snackbar
21.08.2024 | https://youtu.be/KFazs62lIkE |
Всплывающее уведомление в нижней части экрана.
class MyViewModel: ViewModel() {
var isSnackbarShowed: Boolean by mutableStateOf(false)
fun showSnackbarMessage() {
isSnackbarShowed = true
}
fun hideSnackbarMessage() {
isSnackbarShowed = false
}
}
@Composable
fun Composable(
viewModel: MyViewModel = hiltViewModel()
) {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
val isSnackbarShowed = viewModel.isSnackbarShowed
val onShowSnackbar: (String) -> Unit = { message ->
scope.launch {
snackbarHostState.currentSnackbarData?.dismiss()
val snackbarResult = snackbarHostState.showSnackbar(
message = message,
duration = SnackbarDuration.Short
)
if (snackbarResult == SnackbarResult.Dismissed) {
viewModel.hideSnackbarMessage()
}
}
}
if (isSnackbarShowed) {
onShowSnackbar(stringResource(R.string.message))
}
}
Scaffold(
snackbarHost = {
SnackbarHost(
hostState = snackbarHostState
)
}
) { innerPadding ->
}
Layout
Основной строительный блок для создания пользовательских интерфейсов. Предоставляет гибкий способ размещения и управления дочерними компонентами, позволяя вам создавать кастомные интерфейсы и реализовывать сложную логику компоновки.
• Каждый Layout
проходит два этапа: измерение и компоновка.
• Измерение: На этом этапе вы определяете размеры дочерних компонентов, используя метод measure
. Вы можете использовать переданные ограничения (constraints
), чтобы измерить каждый дочерний элемент и получить его размеры.
• Компоновка: После измерения происходит этап компоновки, где вы размещаете дочерние элементы на экране. Этот процесс управляется методом layout
, где вы указываете координаты размещения для каждого элемента.
// Размещает дочерние элементы в строку с заданным интервалом.
@Composable
fun CustomRowLayout(
modifier: Modifier = Modifier,
spacing: Int = 8,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
val placeables = measurables.map { measurable ->
measurable.measure(constraints) // Измеряет размеры дочерних элементов и вычисляет общий размер для размещения.
}
val totalWidth = placeables.sumOf { it.width } + (spacing * (placeables.size - 1))
val maxHeight = placeables.maxOfOrNull { it.height } ?: 0
layout(totalWidth, maxHeight) { // Размещает элементы в строке, учитывая заданный интервал между ними.
var xPosition = 0
placeables.forEach { placeable ->
placeable.place(xPosition, 0)
xPosition += placeable.width + spacing
}
}
}
}
@Composable
fun MyScreen() {
CustomRowLayout(spacing = 16) {
Box(modifier = Modifier.size(50.dp).background(Color.Red))
Box(modifier = Modifier.size(50.dp).background(Color.Green))
Box(modifier = Modifier.size(50.dp).background(Color.Blue))
}
}
Constraints
• В Compose вы работаете с Constraints
, которые описывают размеры и ограничения, в которых дочерние элементы могут быть размещены.
• Constraints
могут иметь минимальные и максимальные значения для ширины и высоты, которые определяют допустимые размеры элементов.
Placeables
• Placeable
— это результат измерения дочернего элемента. Он содержит информацию о размерах и может быть размещен в определенных координатах на экране.
Вопросы на собесе (8)
LazyColumn (4)
- Для чего используется key в LazyColumn?
Для указания уникального идентификатора каждого элемента списка. Это позволяет Compose эффективно отслеживать изменения, обновлять только измененные элементы и сохранять состояние элементов при их добавлении, удалении или изменении порядка.
- Что будет если не задавать ключ в LazyColumn?
Без ключа в
LazyColumn
элементы могут перерисовываться и терять свое состояние при прокрутке, так как Compose не сможет эффективно отслеживать уникальные элементы.
- LazyColumn лагает при прокрутке. Что будем делать?
• Оптимизируйте элементы списка, минимизируя их размер и сложность.
• Используйте
remember
для кэширования состояния элементов.• Избегайте ненужных повторных пересчетов, применяя
key
для элементов списка.• Проверьте, нет ли тяжелых операций в
item
илиitemContent
.
- Разница между LazyColumn и Column?
LazyColumn
рендерит только видимые элементы, оптимизируя память и производительность для длинных списков, тогда какColumn
отображает все элементы сразу, что может быть менее эффективно для больших данных.LazyColumn
лучше подходит для работы с длинными списками.
- Для чего используется key в LazyColumn?
FlowRow (1)
- Для чего используется FlowRow?
Располагает элементы в строках, которые автоматически переносятся на новую строку при переполнении ширины.
- Для чего используется FlowRow?
Другие (3)
- Какие основные Layout есть в Compose?
Box
Row
Column
- Как сделать отступ у Box?
Используйте модификатор
padding
.
- Как создать собственную View в Compose используя Layout?
Использовать компонент
Layout
, который позволяет определить, как измерять и размещать дочерние элементы. Внутри функцииLayout
следует реализовать логику измерения (методmeasure
) и размещения (методlayout
) дочерних компонентов, учитывая их размеры и необходимые отступы. Используя кастомныйLayout
, вы можете контролировать, как будут отображаться и взаимодействовать ваши элементы, обеспечивая необходимую гибкость в дизайне интерфейса.
- Какие основные Layout есть в Compose?