Android Jetpack Compose 中的底部导航栏
我们都在很多应用程序中看到过BottomNavigationBar,比如Instagram、Quora。在本文中,我们将学习如何在 Jetpack Compose 中添加底部导航。下面是它的外观示例。
为什么我们需要底部导航栏?
- 它允许用户轻松切换到不同的活动/片段。
- 它使用户了解应用程序中可用的不同屏幕。
- 用户能够检查他们目前在哪个屏幕上。
下面是底部导航栏的解剖图:
先决条件:
- Jetpack Compose 的知识。
- Jetpack compose 中的 Scaffold 知识。
- 撰写导航的知识。
分步实施
第 1 步:创建一个新项目(或在现有 Compose 项目中使用它)
在 Android Studio Canary 版本中创建一个新项目。参考这篇文章:如何使用 Jetpack Compose 在 Android Studio Canary 版本中创建一个新项目?
第 2 步:添加依赖项
打开 build.gradle(app) 并添加以下依赖项。
implementation “androidx.navigation:navigation-compose:2.4.0-alpha07”
第 3 步:创建屏幕
打开Screens.kt并创建三个屏幕,主页、搜索、配置文件。
主屏幕:
Kotlin
@Composable
fun HomeScreen() {
// Column Composable,
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
// Parameters set to place the items in center
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Icon Composable
Icon(
imageVector = Icons.Default.Home,
contentDescription = "home",
tint = Color(0xFF0F9D58)
)
// Text to Display the current Screen
Text(text = "Home", color = Color.Black)
}
}
Kotlin
@Composable
fun SearchScreen() {
// Column Composable,
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
// parameters set to place the items in center
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Icon Composable
Icon(
imageVector = Icons.Default.Search,
contentDescription = "search",
tint = Color(0xFF0F9D58)
)
// Text to Display the current Screen
Text(text = "Search", color = Color.Black)
}
}
Kotlin
@Composable
fun ProfileScreen() {
// Column Composable,
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
// parameters set to place the items in center
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Icon Composable
Icon(
imageVector = Icons.Default.Person,
contentDescription = "Profile",
tint = Color(0xFF0F9D58)
)
// Text to Display the current Screen
Text(text = "Profile", color = Color.Black)
}
}
Kotlin
import androidx.compose.ui.graphics.vector.ImageVector
data class BottomNavItem(
val label: String,
val icon: ImageVector,
val route:String,
)
Kotlin
object Constants {
val BottomNavItems = listOf(
BottomNavItem(
label = "Home",
icon = Icons.Filled.Home,
route = "home"
),
BottomNavItem(
label = "Search",
icon = Icons.Filled.Search,
route = "search"
),
BottomNavItem(
label = "Profile",
icon = Icons.Filled.Person,
route = "profile"
)
)
}
Kotlin
@Composable
fun NavHostContainer(
navController: NavHostController,
padding: PaddingValues
) {
NavHost(
navController = navController,
// set the start destination as home
startDestination = "home",
// Set the padding provided by scaffold
modifier = Modifier.padding(paddingValues = padding),
builder = {
// route : Home
composable("home") {
HomeScreen()
}
// route : search
composable("search") {
SearchScreen()
}
// route : profile
composable("profile") {
ProfileScreen()
}
})
}
Kotlin
@Composable
fun BottomNavigationBar(navController: NavHostController) {
BottomNavigation(
// set background color
backgroundColor = Color(0xFF0F9D58)) {
// observe the backstack
val navBackStackEntry by navController.currentBackStackEntryAsState()
// observe current route to change the icon
// color,label color when navigated
val currentRoute = navBackStackEntry?.destination?.route
// Bottom nav items we declared
Constants.BottomNavItems.forEach { navItem ->
// Place the bottom nav items
BottomNavigationItem(
// it currentRoute is equal then its selected route
selected = currentRoute == navItem.route,
// navigate on click
onClick = {
navController.navigate(navItem.route)
},
// Icon of navItem
icon = {
Icon(imageVector = navItem.icon, contentDescription = navItem.label)
},
// label
label = {
Text(text = navItem.label)
},
alwaysShowLabel = false
)
}
}
}
Kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BottomNavigationTheme {
// remember navController so it does not
// get recreated on recomposition
val navController = rememberNavController()
Surface(color = Color.White) {
// Scaffold Component
Scaffold(
// Bottom navigation
bottomBar = {
BottomNavigationBar(navController = navController)
}, content = { padding ->
// Navhost: where screens are placed
NavHostContainer(navController = navController, padding = padding)
}
)
}
}
}
}
}
搜索屏幕:
科特林
@Composable
fun SearchScreen() {
// Column Composable,
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
// parameters set to place the items in center
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Icon Composable
Icon(
imageVector = Icons.Default.Search,
contentDescription = "search",
tint = Color(0xFF0F9D58)
)
// Text to Display the current Screen
Text(text = "Search", color = Color.Black)
}
}
配置文件屏幕:
科特林
@Composable
fun ProfileScreen() {
// Column Composable,
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
// parameters set to place the items in center
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Icon Composable
Icon(
imageVector = Icons.Default.Person,
contentDescription = "Profile",
tint = Color(0xFF0F9D58)
)
// Text to Display the current Screen
Text(text = "Profile", color = Color.Black)
}
}
第 4 步:创建底部导航项目
让我们创建一个数据类来保存与底部导航项(如标签、图标、路线)相关的数据。打开模型/BottomNavItem.kt并添加以下代码。
科特林
import androidx.compose.ui.graphics.vector.ImageVector
data class BottomNavItem(
val label: String,
val icon: ImageVector,
val route:String,
)
并创建一些底部导航项目,打开 Utils/ Constants.kt , 以及三个导航项目的列表。
科特林
object Constants {
val BottomNavItems = listOf(
BottomNavItem(
label = "Home",
icon = Icons.Filled.Home,
route = "home"
),
BottomNavItem(
label = "Search",
icon = Icons.Filled.Search,
route = "search"
),
BottomNavItem(
label = "Profile",
icon = Icons.Filled.Person,
route = "profile"
)
)
}
第 5 步:使用 MainActivity 和导航组件
在MainActivity.kt 中创建一个名为NavHostContainer的函数,它将包含 NavHost 和用于导航的 Composable。参考代码中的注释更好理解
科特林
@Composable
fun NavHostContainer(
navController: NavHostController,
padding: PaddingValues
) {
NavHost(
navController = navController,
// set the start destination as home
startDestination = "home",
// Set the padding provided by scaffold
modifier = Modifier.padding(paddingValues = padding),
builder = {
// route : Home
composable("home") {
HomeScreen()
}
// route : search
composable("search") {
SearchScreen()
}
// route : profile
composable("profile") {
ProfileScreen()
}
})
}
第 6 步:添加底部导航
请参阅注释以获得更好的理解。
科特林
@Composable
fun BottomNavigationBar(navController: NavHostController) {
BottomNavigation(
// set background color
backgroundColor = Color(0xFF0F9D58)) {
// observe the backstack
val navBackStackEntry by navController.currentBackStackEntryAsState()
// observe current route to change the icon
// color,label color when navigated
val currentRoute = navBackStackEntry?.destination?.route
// Bottom nav items we declared
Constants.BottomNavItems.forEach { navItem ->
// Place the bottom nav items
BottomNavigationItem(
// it currentRoute is equal then its selected route
selected = currentRoute == navItem.route,
// navigate on click
onClick = {
navController.navigate(navItem.route)
},
// Icon of navItem
icon = {
Icon(imageVector = navItem.icon, contentDescription = navItem.label)
},
// label
label = {
Text(text = navItem.label)
},
alwaysShowLabel = false
)
}
}
}
第 7 步:将所有内容放在 Scaffold 中
现在我们需要把所有的东西都放在MainActivity类的 setContent 中
科特林
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BottomNavigationTheme {
// remember navController so it does not
// get recreated on recomposition
val navController = rememberNavController()
Surface(color = Color.White) {
// Scaffold Component
Scaffold(
// Bottom navigation
bottomBar = {
BottomNavigationBar(navController = navController)
}, content = { padding ->
// Navhost: where screens are placed
NavHostContainer(navController = navController, padding = padding)
}
)
}
}
}
}
}
输出:
项目链接:点我