Разработка
Изогнутое нижнее меню в Jetpack Compose
Для получения округлой формы мы используем кубические кривые Безье.
Сегодня я покажу вам, как создать изогнутое нижнее меню в Jetpack Compose.
Для начала давайте создадим класс, определяющий маршрут (элемент меню).
data class Screen(
val route: String,
val icon: Int,
val selectedIcon: Int,
)
Я использую icon
и selectedIcon
, потому что в моей реализации они меняются, но вы можете изменить оттенок на любой другой.
Теперь давайте определим bottom bar.
xxxxxxxxxx
fun BottomMenuBar(
screens: List<Screen>,
currentScreen: Screen?,
onNavigateTo: (Screen) -> Unit,
)
screens
— это маршруты, которые вы хотите отобразить в нижней панели. Вам нужно указать 5 экранов, чтобы эта нижняя панель работала.currentScreen
— это текущий выбранный экран, чтобы мы могли изменить его иконку.onNavigateTo
вызывается при нажатии на иконку в нижней панели.
Прежде чем продолжить, давайте создадим округлую форму, которая будет использоваться в качестве фона.
xxxxxxxxxx
private fun menuBarShape() = GenericShape { size, _ ->
reset()
moveTo(0f, 0f)
val width = 150f
val height = 90f
val point1 = 75f
val point2 = 85f
lineTo(size.width / 2 - width, 0f)
cubicTo(
size.width / 2 - point1, 0f,
size.width / 2 - point2, height,
size.width / 2, height
)
cubicTo(
size.width / 2 + point2, height,
size.width / 2 + point1, 0f,
size.width / 2 + width, 0f
)
lineTo(size.width / 2 + width, 0f)
lineTo(size.width, 0f)
lineTo(size.width, size.height)
lineTo(0f, size.height)
close()
}
Для получения округлой формы мы используем кубические кривые Безье. Если вы хотите изменить округлую часть, вы можете изменить width
, height
, point1
и point2
. При изменении этих переменных кривизна будет меняться.
Теперь давайте определим предварительный просмотр, чтобы мы могли видеть нижнее меню при его изменении.
xxxxxxxxxx
showSystemUi = true) (
private fun Preview() {
var currentScreen by remember { mutableStateOf<Screen?>(null) }
Box(
contentAlignment = Alignment.BottomCenter,
modifier = Modifier.fillMaxSize()
) {
BottomMenuBar(
screens = listOf(
Screen(
route = "home",
icon = R.drawable.outline_home_24,
selectedIcon = R.drawable.baseline_home_24,
),
Screen(
route = "products",
icon = R.drawable.outline_collections_24,
selectedIcon = R.drawable.baseline_collections_24,
),
Screen(
route = "cart",
icon = R.drawable.outline_shopping_cart_24,
selectedIcon = R.drawable.baseline_shopping_cart_24,
),
Screen(
route = "profile",
icon = R.drawable.outline_person_24,
selectedIcon = R.drawable.baseline_person_24,
),
Screen(
route = "chat",
icon = R.drawable.outline_chat_24,
selectedIcon = R.drawable.baseline_chat_24,
),
),
currentScreen = currentScreen,
onNavigateTo = { currentScreen = it },
)
}
}
Переменная currentScreen
в предварительном просмотре используется для того, чтобы мы могли играть с нижнем меню и видеть, как оно меняется.
Теперь давайте реализуем сам боттом бар.
xxxxxxxxxx
fun BottomMenuBar(
screens: List<Screen>,
currentScreen: Screen?,
onNavigateTo: (Screen) -> Unit,
) {
val backgroundShape = remember { menuBarShape() }
Box {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.background(Color.White, backgroundShape)
.align(Alignment.BottomCenter)
)
Column(
modifier = Modifier
.align(Alignment.TopCenter)
) {
FloatingActionButton(
shape = RoundedCornerShape(50),
containerColor = Color.White,
contentColor = Color.Gray,
onClick = {},
modifier = Modifier.clip(RoundedCornerShape(50))
) {
Row(
modifier = Modifier.size(64.dp)
) {
BottomBarItem(screens[2], currentScreen, onNavigateTo)
}
}
Spacer(modifier = Modifier.height(30.dp))
}
Row(
modifier = Modifier
.height(56.dp)
.align(Alignment.BottomCenter)
) {
BottomBarItem(screens[0], currentScreen, onNavigateTo)
BottomBarItem(screens[1], currentScreen, onNavigateTo)
Spacer(modifier = Modifier.width(72.dp))
BottomBarItem(screens[3], currentScreen, onNavigateTo)
BottomBarItem(screens[4], currentScreen, onNavigateTo)
}
}
}
Нам также необходимо реализовать компонент для пунктов меню,
xxxxxxxxxx
private fun RowScope.BottomBarItem(
screen: Screen,
currentScreen: Screen?,
onNavigateTo: (Screen) -> Unit,
) {
val selected = currentScreen?.route == screen.route
Box(
Modifier
.selectable(
selected = selected,
onClick = { onNavigateTo(screen) },
role = Role.Tab,
interactionSource = remember { MutableInteractionSource() },
indication = remember { ripple(radius = 32.dp) }
)
.fillMaxHeight()
.weight(1f),
contentAlignment = Alignment.Center
) {
BadgedBox(
badge = {},
content = {
Image(
painter = painterResource(
id = when {
selected -> screen.selectedIcon
else -> screen.icon
}
),
contentDescription = null
)
},
)
}
}
Ну, вот и все. У вас есть рабочее нижнее меню с изогнутым элементом.
-
Программирование4 недели назад
Конец программирования в том виде, в котором мы его знаем
-
Видео и подкасты для разработчиков7 дней назад
Как устроена мобильная архитектура. Интервью с тех. лидером юнита «Mobile Architecture» из AvitoTech
-
Магазины приложений3 недели назад
Магазин игр Aptoide запустился на iOS в Европе
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2025.8