正确方案:以 LoginFragment 为入口,按需导航
1. 修改导航图(res/navigation/mobile_navigation.xml)
把 `LoginFragment` 设为 `startDestination`,并定义显式跳转动作:
```xml
```
值得警惕的是,`app:popUpToInclusive="true"` 这个属性很关键——它保证登录成功后 `LoginFragment` 会被从返回栈里彻底移除,用户按返回键时就不会再回到登录页了。
2. 在 LoginFragment 中实现登录逻辑与状态验证
别再用 `MainActivity.isLogin()` 那种占位符逻辑(目前恒返回 false 显然是不对的)。登录成功后必须持久化凭证,比如用 `EncryptedSharedPreferences` 或者 Jetpack Security 来存储。然后在 `onViewCreated()` 里主动检查一下:
```kotlin
// LoginFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle) {
super.onViewCreated(view, savedInstanceState)
// 检查是否已登录(读取本地凭证)
val isLoggedIn = requireContext().getLoginStatus()
if (isLoggedIn) {
findNavController().navigate(R.id.action_login_to_home)
return
}
binding.loginButton.setOnClickListener {
val username = binding.loginUsername.text.toString().trim()
val password = binding.loginPassword.text.toString().trim()
if (validateCredentials(username, password)) {
saveLoginState(username) // 持久化
findNavController().navigate(R.id.action_login_to_home)
}
}
}
```
3. 主 Activity 中移除冗余 UI 隐藏逻辑
过去那种在 `MainActivity.onCreate()` 里手动隐藏 `Toolbar` 和 `BottomNavigationView` 的做法,既不推荐也不可靠。因为 `NavHostFragment` 已经加载了 `HomeFragment`,UI 元素可能已经被渲染了一遍,你再隐藏它,只会造成视觉闪烁。
正确的思路是:**让 Navigation 组件自己控制 UI 可见性**。利用 `AppBarConfiguration` 的多顶层目的地特性,把 `LoginFragment` 和 `HomeFragment` 放在不同的导航上下文里:
```java
// MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
BottomNavigationView navView = findViewById(R.id.nav_view);
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
// 关键:为 LoginFragment 和 HomeFragment 分别定义 AppBar 行为
AppBarConfiguration appBarConfig = new AppBarConfiguration.Builder(
R.id.loginFragment, // ← 登录页也是顶层目的地(无向上按钮)
R.id.homeFragment,
R.id.navigation_direct,
R.id.navigation_groups,
R.id.navigation_settings)
.build();
NavigationUI.setupWithNavController(toolbar, navController, appBarConfig);
NavigationUI.setupWithNavController(navView, navController);
}
```
这样一来,当用户位于 `LoginFragment` 时,`BottomNavigationView` 会自动隐藏(因为它不是底部导航的目标),`Toolbar` 也不会显示向上箭头;而一旦进入 `HomeFragment`,底部导航栏便恢复正常显示。这才是既干净又可靠的做法。
4. 安全增强建议
- **禁止后台进程绕过登录**:在 `MainActivity.onResume()` 里再次校验登录状态,如果发现失效,立即重定向至登录页。
- **退出登录处理**:在 `SettingsFragment` 或侧边栏中提供“退出登录”按钮,清除凭证后调用 `navController.navigate(R.id.loginFragment)`。
- **Google Play 游戏服务集成提示**:如果打算接入 Google Play 登录,得注意 v1 SDK 在 2025 年 2 月已经停止新发布支持了,务必迁移到 v2 SDK。它的 `GoogleSignInClient` 必须配合 `DEFAULT_GAMES_SIGN_IN` 选项初始化,并通过 `GoogleSignIn.getLastSignedInAccount()` 安全校验登录态。
经过这样一套结构化的改造,你的应用就能严格遵循“先认证、后访问”的原则。不仅保障了安全性,也符合 Material Design 导航规范,同时避免了 UI 闪烁和状态不一致这些常见陷阱。