Connect with us

Разработка

Изогнутое нижнее меню в Jetpack Compose

Для получения округлой формы мы используем кубические кривые Безье.

Опубликовано

/

     
     

Сегодня я покажу вам, как создать изогнутое нижнее меню в Jetpack Compose.

Для начала давайте создадим класс, определяющий маршрут (элемент меню).

data class Screen(
    val route: String,
    @DrawableRes val icon: Int,
    @DrawableRes val selectedIcon: Int,
)

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

Теперь давайте определим bottom bar.

@Composable
fun BottomMenuBar(
    screens: List<Screen>,
    currentScreen: Screen?,
    onNavigateTo: (Screen) -> Unit,
)
  • screens — это маршруты, которые вы хотите отобразить в нижней панели. Вам нужно указать 5 экранов, чтобы эта нижняя панель работала.
  • currentScreen — это текущий выбранный экран, чтобы мы могли изменить его иконку.
  • onNavigateTo вызывается при нажатии на иконку в нижней панели.

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

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. При изменении этих переменных кривизна будет меняться.

Теперь давайте определим предварительный просмотр, чтобы мы могли видеть нижнее меню при его изменении.

@Preview(showSystemUi = true)
@Composable
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 в предварительном просмотре используется для того, чтобы мы могли играть с нижнем меню и видеть, как оно меняется.

Теперь давайте реализуем сам боттом бар.

@Composable
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)
        }
    }
}

Нам также необходимо реализовать компонент для пунктов меню,

@Composable
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
                )
            },
        )
    }
}

Ну, вот и все. У вас есть рабочее нижнее меню с изогнутым элементом.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.

Наши партнеры:

LEGALBET

Мобильные приложения для ставок на спорт
Хорошие новости

Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: