对比iOS和flutter,Jetpack Compose的底部导航有自己的特色,更像flutter的底部导航。按照官网提供的方式结合项目实例,完成了小功能的实现:
1.创建好脚手架Scallfold函数和对应的选中图标和未选中标题,以及对应的颜色。
2.创建每个item对应的Composable函数界面
3.配合官网建议,利用密封类和导航来进行底部不同界面的切换
@ExperimentalFoundationApi
@Composable
fun Tab(viewModel: ViewMod){
// tab标题
val listItem = listOf("组织","发现","我的")
// tab未选图标
val icons = listOf(R.drawable.home,R.drawable.find,R.drawable.profile)
// tab选中图标
val selectIcons = listOf(R.drawable.homeselect,R.drawable.findselect,R.drawable.profileselect)
// 记住选中tab位置
val selectIndex = remember {
mutableStateOf(0)
}
// 脚手架
val navControllers = rememberNavController()
Scaffold(
modifier = Modifier.fillMaxSize(),
bottomBar = {
// BottomNavigation的显示和隐藏先借助本地数据存储,再说
BottomNavigation(
backgroundColor = Color.White,
elevation = 3.dp
) {
val navBackStackEntry by navControllers.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE)
items.forEachIndexed { index, s ->
BottomNavigationItem(
selected = currentRoute == s.route,
onClick = {
selectIndex.value = index
navControllers.navigate(s.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo = navControllers.graph.startDestination
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
}
},
icon = {
when(index){
selectIndex.value -> {
Image(painter = painterResource(id = selectIcons[index]), contentDescription = null)
}
else -> {
Image(painter = painterResource(id = icons[index]), contentDescription = null)
}
}
},
label = {
Text(
text = listItem[index],
textAlign = TextAlign.Center,
color = if (index == selectIndex.value) Color(0xFF0077E6) else Color(0xFFD8D8D8)
)
}
)
}
}
},
content = {
NavHost(navControllers, startDestination = Screen.Home.route) {
// 首页
composable(Screen.Home.route) { Home(viewModel,navControllers) }
// 发现
composable(Screen.Find.route) { Find(navControllers) }
// 我的
composable(Screen.Profile.route) { Profile(navControllers,viewModel) }
}
}
)
}
sealed class Screen(val route: String, @StringRes val resourceId: Int) {
object Home : Screen("home", R.string.home)
object Find : Screen("find", R.string.find)
object Profile : Screen("profile", R.string.profile)
}
val items = listOf(
Screen.Home,
Screen.Find,
Screen.Profile
)
之所以用密封类和导航来进行界面的切换,是为了减少界面的重复创建和资源的消耗浪费。比如根据切换的位置进行界面的切换(不建议):
content = {
when(selectIndex){
0 -> Home()
1 -> Find()
else -> Profile()
}