Android Java 底部导航
创建 activity_main.xml 布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/home_container"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
app:layout_constraintBottom_toTopOf="@+id/bottom_tab_layout"
android:layout_height="0dp" />
<View android:layout_width="match_parent"
android:layout_height="0.5dp"
android:alpha="0.6"
app:layout_constraintBottom_toTopOf="@+id/bottom_tab_layout"
android:background="@android:color/darker_gray"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/bottom_tab_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
app:tabIndicatorHeight="0dp"
app:tabPaddingTop="4dp"
app:tabPaddingBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:tabSelectedTextColor="@android:color/black"
app:tabTextColor="@android:color/darker_gray"/>
</androidx.constraintlayout.widget.ConstraintLayout>
创建 HomeFragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="Fragment Home"
android:textSize="28sp"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.bottom.navigation;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class HomeFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
}
创建 MineFragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="Fragment Mine"
android:textSize="28sp"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.bottom.navigation;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class MineFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_mine, container, false);
}
}
创建 MainActivity 实现导航切换 Fragment
package com.bottom.navigation;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.widget.FrameLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.material.tabs.TabLayout;
public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSelectedListener {
private FrameLayout mFrame;
private TabLayout mTab;
private HomeFragment mHomeFragment;
private MineFragment mMineFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initTab(); //设置TabLayout的标题
initFragmentReplace();// 设置开启页面时fragment的显示和影藏
mTab.addOnTabSelectedListener(this);// 设置TabLayout选择监听
}
private void initTab() {
mTab.addTab(mTab.newTab().setText("首页").setIcon(R.drawable.ic_home));
mTab.addTab(mTab.newTab().setText("上传").setIcon(R.drawable.ic_mine));
mTab.getTabAt(0).select();
mTab.getTabAt(0).getIcon().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
mTab.getTabAt(1).getIcon().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
}
private void initFragmentReplace() {
// 获取到fragment碎片管理器
FragmentManager manager = getSupportFragmentManager();
// 开启事务
FragmentTransaction transaction = manager.beginTransaction();
// 获取到fragment的对象
mHomeFragment = new HomeFragment();
mMineFragment = new MineFragment();
// 设置要显示的fragment 和 隐藏的fragment
transaction.add(R.id.home_container, mHomeFragment, "home").show(mHomeFragment);
transaction.add(R.id.home_container, mMineFragment, "mine").hide(mMineFragment);
// 提交事务
transaction.commit();
}
private void initView() {
// 获取控件对象
mTab = (TabLayout) findViewById(R.id.bottom_tab_layout);
mFrame = (FrameLayout) findViewById(R.id.home_container);
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
// 设置选中时fragment的显示和影藏
switch (tab.getPosition()) {
case 0:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.show(mHomeFragment).hide(mMineFragment).commit();
mTab.getTabAt(0).getIcon().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
mTab.getTabAt(1).getIcon().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
break;
case 1:
FragmentManager fragmentManager1 = getSupportFragmentManager();
FragmentTransaction transaction1 = fragmentManager1.beginTransaction();
transaction1.show(mMineFragment).hide(mHomeFragment).commit();
mTab.getTabAt(0).getIcon().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
mTab.getTabAt(1).getIcon().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
break;
default:
break;
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
}
预览布局
创建 Bottom Navigation Views Activity
使用 Android Studio 创建
配置 org.jetbrains.kotlin:kotlin-bom 依赖
创建完成后代码结构
预览布局
Android Kotlin 底部导航
创建 HomeFragment
package com.kotlin.bottom.navigation
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
class HomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
}
创建 MineFragment
package com.kotlin.bottom.navigation
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
class MineFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_mine, container, false)
}
}
创建 MainActivity 实现导航切换 Fragment
package com.kotlin.bottom.navigation
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
class MainActivity : AppCompatActivity(), OnTabSelectedListener {
private var mFrame: FrameLayout? = null
private var mTab: TabLayout? = null
private var mHomeFragment: HomeFragment? = null
private var mMineFragment: MineFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
initTab() //设置TabLayout的标题
initFragmentReplace() // 设置开启页面时fragment的显示和影藏
mTab!!.addOnTabSelectedListener(this) // 设置TabLayout选择监听
}
private fun initTab() {
mTab!!.addTab(mTab!!.newTab().setText("首页").setIcon(R.drawable.ic_home))
mTab!!.addTab(mTab!!.newTab().setText("上传").setIcon(R.drawable.ic_mine))
mTab!!.getTabAt(0)!!.select()
mTab!!.getTabAt(0)!!.icon!!.colorFilter= PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN)
mTab!!.getTabAt(1)!!.icon!!.colorFilter= PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
}
private fun initFragmentReplace() {
// 获取到fragment碎片管理器
val manager = supportFragmentManager
// 开启事务
val transaction = manager.beginTransaction()
// 获取到fragment的对象
mHomeFragment = HomeFragment()
mMineFragment = MineFragment()
// 设置要显示的fragment 和 隐藏的fragment
transaction.add(R.id.home_container, mHomeFragment!!, "home").show(mHomeFragment!!)
transaction.add(R.id.home_container, mMineFragment!!, "mine").hide(mMineFragment!!)
// 提交事务
transaction.commit()
}
private fun initView() {
// 获取控件对象
mTab = findViewById<View>(R.id.bottom_tab_layout) as TabLayout
mFrame = findViewById<View>(R.id.home_container) as FrameLayout
}
override fun onTabSelected(tab: TabLayout.Tab) {
// 设置选中时fragment的显示和影藏
when (tab.position) {
0 -> {
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.show(mHomeFragment!!).hide(mMineFragment!!).commit()
mTab!!.getTabAt(0)!!.icon!!.colorFilter= PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN)
mTab!!.getTabAt(1)!!.icon!!.colorFilter= PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
}
1 -> {
val fragmentManager1 =
supportFragmentManager
val transaction1 = fragmentManager1.beginTransaction()
transaction1.show(mMineFragment!!).hide(mHomeFragment!!).commit()
mTab!!.getTabAt(0)!!.icon!!.colorFilter= PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
mTab!!.getTabAt(1)!!.icon!!.colorFilter= PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN)
}
else -> {}
}
}
override fun onTabUnselected(tab: TabLayout.Tab) {}
override fun onTabReselected(tab: TabLayout.Tab) {}
}
IOS Object-c实现底部导航
创建底部导航并设置为根视图
修改底部导航图标大小
配置依赖 UIImage-Resize
pod 'UIImage-Resize'
修改图片大小
自定义底部导航样式
预览底部导航布局
仿淘宝闲鱼的TabBar
预览布局
IOS Swift实现底部导航
创建底部导航并设置为根视图
定义修改UIImage大小的函数
修改底部导航图标大小
预览底部导航布局
鸿蒙底部导航
添加导航使用的图标
创建底部导航
@Entry
@Component
struct Index {
@State currentIndex: number = 0
private controller: TabsController = new TabsController()
// 自定义导航页签的样式
@Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
Column() {
Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
.size({ width: 25, height: 25 })
Text(title)
.fontColor(this.currentIndex === targetIndex ? '#28bff1' : '#8a8a8a')
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentIndex = targetIndex
this.controller.changeIndex(this.currentIndex)
})
}
build() {
Column() {
Tabs({
barPosition: BarPosition.End,
controller: this.controller
}) {
TabContent() {
Column() {
// 标题栏
Text("首页")
.size({ width: '100%', height: 50 })
.backgroundColor("#28bff1")
.fontColor("#ffffff")
.textAlign(TextAlign.Center)
.fontSize("18fp")
// 内容项
Text("首页").width('100%').height('100%').textAlign(TextAlign.Center).fontSize("25fp")
}.size({ width: '100%', height: '100%' })
}.tabBar(this.TabBuilder('首页', 0, $r('app.media.icon_indexed'), $r('app.media.icon_index')))
TabContent() {
Column() {
// 标题栏
Text("列表")
.size({ width: '100%', height: 50 })
.backgroundColor("#28bff1")
.fontColor("#ffffff")
.textAlign(TextAlign.Center)
.fontSize("18fp")
Text("列表").width('100%').height('100%').textAlign(TextAlign.Center).fontSize("25fp")
}.size({ width: '100%', height: '100%' })
}.tabBar(this.TabBuilder('列表', 1, $r('app.media.icon_listed'), $r('app.media.icon_list')))
TabContent() {
Column() {
// 标题栏
Text("更多")
.size({ width: '100%', height: 50 })
.backgroundColor("#28bff1")
.fontColor("#ffffff")
.textAlign(TextAlign.Center)
.fontSize("18fp")
Text("更多").width('100%').height('100%').textAlign(TextAlign.Center).fontSize("25fp")
}.size({ width: '100%', height: '100%' })
}.tabBar(this.TabBuilder('更多', 2, $r('app.media.icon_othered'), $r('app.media.icon_other')))
}.scrollable(false) // 禁止滑动切换
}
.width('100%')
.height('100%')
}
}
预览导航
React Native 底部导航
执行如下命令安装相关依赖:参考
yarn add @react-navigation/native
yarn add @react-navigation/bottom-tabs
yarn add --save react-native-vector-icons
实现底部导航
import {name as appName} from './app.json';
import * as React from 'react';
import { Button, View, AppRegistry,Text } from 'react-native';
import { NavigationContainer,useNavigation } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/FontAwesome';
import Ionicons from 'react-native-vector-icons/Ionicons'
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen}
options={{
tabBarIcon: ({ focused }) => (
<Ionicons focused={focused}
name="home"
color={focused ? 'blue' : 'gray'}
size={30}
/>
),
}}
/>
<Tab.Screen name="Profile" component={ProfileScreen}
options={{
tabBarIcon: ({ focused }) => (
<Ionicons
focused={focused}
name="settings"
color={focused ? 'blue' : 'gray'}
size={30}
/>
),
}}
/>
</Tab.Navigator>
);
}
function HomeScreen() {
const navigation = useNavigation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile')}
/>
</View>
);
}
function ProfileScreen() {
const navigation = useNavigation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</View>
);
}
const MyApp =() => {
return (
<NavigationContainer>
<MyTabs />
</NavigationContainer>
);
}
AppRegistry.registerComponent(appName, () => MyApp);
预览导航
Flutter 底部导航
实现底部导航
import 'package:flutter/material.dart';
import 'package:flutter_bottom_navigation/navigation_bar_widget.dart';
import 'package:flutter_bottom_navigation/stack_widget.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, this.title}) : super(key: key);
final String? title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<StackWidgetState> _stackGk = GlobalKey<StackWidgetState>();
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title!),
),
bottomNavigationBar: NavigationBarWidget(
stackValue: (int currentIndex, List<int> tabInt) {
_stackGk.currentState?.changeStack(currentIndex, tabInt);
},
),
body: StackWidget(
key: _stackGk,
));
}
}
import 'package:flutter/material.dart';
class NavigationBarWidget extends StatefulWidget {
final ValueChanged? stackValue;
const NavigationBarWidget({Key? key, this.stackValue}) : super(key: key);
@override
_BotNavBarWidgetState createState() => _BotNavBarWidgetState();
}
class _BotNavBarWidgetState extends State<NavigationBarWidget> {
int _currentIndex = 0;
List<int> tabInt = [0];
final List<BottomNavigationBarItem> _botNavBarItems =
<BottomNavigationBarItem>[
const BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Page1'),
const BottomNavigationBarItem(icon: Icon(Icons.payment), label: 'Page2'),
const BottomNavigationBarItem(icon: Icon(Icons.monetization_on), label: 'Page3'),
const BottomNavigationBarItem(icon: Icon(Icons.accessibility), label: 'Page4')
];
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: onTabTapped,
currentIndex: _currentIndex,
backgroundColor: Colors.lightBlueAccent,
//selectedItemColor: Colors.black,
unselectedItemColor: Colors.red,
elevation: 0.0,
fixedColor: Colors.black,
showUnselectedLabels: true,
items: _botNavBarItems,
);
}
onTabTapped(int index) {
setState(() {});
_currentIndex = index;
if (!tabInt.contains(index)) {
tabInt.add(index);
}
if (widget.stackValue != null) {
widget.stackValue!(_currentIndex,tabInt);
}
}
}
typedef ValueChanged = void Function(
int currentIndex,
List<int> tabInt,
);
import 'package:flutter/widgets.dart';
import 'package:flutter_bottom_navigation/page_four.dart';
import 'package:flutter_bottom_navigation/page_one.dart';
import 'package:flutter_bottom_navigation/page_three.dart';
import 'package:flutter_bottom_navigation/page_two.dart';
class StackWidget extends StatefulWidget {
StackWidget({Key? key, this.currentIndex = 0, this.tabInt}) : super(key: key);
final int currentIndex;
final List<int>? tabInt;
@override
StackWidgetState createState() => StackWidgetState();
}
class StackWidgetState extends State<StackWidget> {
final List<StatefulWidget> _children = [
const PageOne(
title: "Page1",
),
const PageTwo(
title: "Page2",
),
const PageThree(
title: "Page3",
),
const PageFour(
title: "Page4",
),
];
int _currentIndex = 0;
List<int>? _tabInt;
@override
void initState() {
// TODO: implement initState
super.initState();
_tabInt = widget.tabInt ?? [0];
}
@override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
child: _stack(),
);
}
void changeStack(int currentIndex, List<int> tabInt) {
setState(() {});
_currentIndex = currentIndex;
_tabInt = tabInt;
}
Stack _stack() {
return Stack(
children: <Widget>[
_child(0),
_child(1),
_child(2),
_child(3),
],
);
}
//Page的显示和隐藏
_child(int _index) {
return Offstage(
offstage: !(_currentIndex == _index),
child: _tabInt!.contains(_index) ? _children[_index] : Container(),
);
}
}
预览布局
Android Compose 底部导航
实现底部导航
添加依赖
implementation("androidx.navigation:navigation-compose:2.7.5")
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FavoriteBorder
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material.icons.outlined.ShoppingCart
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
sealed class Screen(val route: String, val title: String, val icon: ImageVector) {
data object Home : Screen("home", "Home", Icons.Outlined.Home)
data object Favorite : Screen("favorite", "Favorite", Icons.Outlined.FavoriteBorder)
data object Profile : Screen("profile", "Profile", Icons.Outlined.Person)
data object Cart : Screen("cart", "Cart", Icons.Outlined.ShoppingCart)
}
val items = listOf(
Screen.Home,
Screen.Favorite,
Screen.Profile,
Screen.Cart
)
@Composable
fun NavBottomBar() {
val navController = rememberNavController()
Column(modifier = Modifier.fillMaxSize()) {
NavHost(
navController,
startDestination = Screen.Home.route,
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
composable(Screen.Home.route) { HomeScreen(navController) }
composable(Screen.Favorite.route) { FavoriteScreen(navController) }
composable(Screen.Profile.route) { ProfileScreen(navController) }
composable(Screen.Cart.route) { CartScreen(navController) }
}
BottomBar(navController, items, modifier = Modifier.fillMaxWidth())
}
}
@Composable
fun BottomBar(
navController: NavHostController,
items: List<Screen>, //导航路线
modifier: Modifier = Modifier
) {
//获取当前的 NavBackStackEntry 来访问当前的 NavDestination
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
Row(
modifier = modifier.background(color = Color.White),
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically
) {
items.forEachIndexed { index, screen ->
BottomBarItem(
item = screen,
//与层次结构进行比较来确定是否被选中
isSelected = currentDestination?.hierarchy?.any { it.route == screen.route },
onItemClicked = {
//加这个可解决问题:按back键会返回2次,第一次先返回home, 第二次才会退出
navController.popBackStack()
//点击item时,清空栈内 popUpTo ID到栈顶之间的所有节点,避免站内节点持续增加
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
//跳转时保存页面状态
saveState = true
}
//栈顶复用,避免重复点击同一个导航按钮,回退栈中多次创建实例
launchSingleTop = true
//回退时恢复页面状态
restoreState = true
//通过使用 saveState 和 restoreState 标志,当在底部导航项之间切换时,
//系统会正确保存并恢复该项的状态和返回堆栈。
}
}
)
}
}
}
@Composable
private fun BottomBarItem(
item: Screen,
isSelected: Boolean?, //是否选中
onItemClicked: () -> Unit, //按钮点击监听
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.clickableWithoutInteraction { onItemClicked.invoke() },
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
modifier = Modifier.size(30.dp),
imageVector = item.icon,
contentDescription = item.title,
tint = if (isSelected == true) Color.Blue else Color.Gray,
)
Text(
text = item.title,
color = if (isSelected == true) Color.Blue else Color.Gray,
fontSize = 10.sp,
)
}
}
@Composable
fun HomeScreen(navController: NavController) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = "HomeScreen"
)
}
}
@Composable
fun FavoriteScreen(navController: NavController) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = "FavoriteScreen"
)
}
}
@Composable
fun ProfileScreen(navController: NavController) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = "ProfileScreen"
)
}
}
@Composable
fun CartScreen(navController: NavController) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = "CartScreen"
)
}
}
/**
* clickable禁用点击涟漪效应
*/
inline fun Modifier.clickableWithoutInteraction(crossinline onClick: () -> Unit): Modifier =
this.composed {
clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
onClick()
}
}