在我的应用程序中,我使用Jetpack Navigation和BottomNavigationView. 我有 4 个片段:主页、搜索、通知、个人资料,当我在主页片段中时,我再次单击底部导航视图中的主页项目,它会重新创建片段。我进行了搜索,但主要的答案是针对那些不使用喷气背包导航的人。
(顺便说一句,我只希望当我已经在该片段中时不重新创建片段,如果我不在该片段中,则可以重新创建)
以下是我的设置:
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainerView_mainActivity) as NavHostFragment
navController = navHostFragment.navController
binding.bottomNavView.setupWithNavController(navController)
Run Code Online (Sandbox Code Playgroud)
android android-fragments fragmentmanager android-bottomnav android-jetpack-navigation
我会尝试做一些 ASCII 艺术来描述这个问题:
<--------------------------------------\
DestinationA --> DestinationC ---------> DestinationE
DestinationB ------/ \-----> DestinationD --/
Run Code Online (Sandbox Code Playgroud)
我希望这是可以解读的。可以从目的地 A 和 B 到达 C。可以从 C 和 D 到达 E。E 返回到 A 或 B(以返回堆栈中的为准)。目的地 C、D 和 E 采用参数 (id)。
实现这一点的最佳方法是什么?使用嵌套导航图看起来是可能的。
以下内容有效,但感觉更像是一种解决方法,而不是导航组件的预期工作方式。
<--------------------------------------\
DestinationA --> DestinationC ---------> DestinationE
DestinationB ------/ \-----> DestinationD --/
Run Code Online (Sandbox Code Playgroud)
NavHost 目前的使用情况是:
val destination = navController.getBackStackEntry("DestinationC/{id}").destination
navController.popBackStack(destination.id, true)
Run Code Online (Sandbox Code Playgroud) 我正在创建一个带有 BottomNavigationView 的应用程序。
我正在使用 JetpackNavigation 和 kotlin。
我已将 XML 设置为
<fragment
android:id="@+id/fragNavHost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/bottomNavView"
app:navGraph="@navigation/bottom_nav_graph"
tools:layout_editor_absoluteX="0dp" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_nav_menu">
Run Code Online (Sandbox Code Playgroud)
但我Replace the <fragment> tag with FragmentContainerView.从 Android Studio 建议中收到一条建议消息
该应用程序在使用 FragmentContainerView 时中断,但在使用 Fragment 时完美运行
我收到的错误是
Unable to start activity ComponentInfo{com.example.reportbusterclone/com.example.reportbusterclone.view.MainActivity}: java.lang.IllegalStateException: Activity com.example.reportbusterclone.view.MainActivity@f462d5a does not have a NavController set on 2131296446
Run Code Online (Sandbox Code Playgroud)
即使只有标签发生变化而没有其他变化。
仅当底部菜单项被单击并且仍然使用 setupWithNavController 时,我们如何跟踪底部菜单导航项的单击?目前使用底部导航视图 2.4.0-alpha05,我们有一个用于跟踪底部导航点击进行分析的用例,底部导航设置如下
binding.bottomNavigationView.setupWithNavController(navController)
Run Code Online (Sandbox Code Playgroud)
如果我在下面使用,那么底部导航选择将停止工作。
binding.bottomNavigationView.setOnItemSelectedListener {
//track clicks
true
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是复制源代码 NavigationUi.setupWithNavController并按如下所示进行更改,但是我不确定这是否会产生影响,例如如果谷歌稍后添加改进或更改 setupWithNavController,那么该项目将错过该更改:
private fun setupWithNavController(
navigationBarView: NavigationBarView,
navController: NavController,
onItemSelected: ((Int) -> Unit)
) {
navigationBarView.setOnItemSelectedListener { item ->
onItemSelected.invoke(item.itemId)
NavigationUI.onNavDestinationSelected(
item,
navController
)
}
val weakReference = WeakReference(navigationBarView)
navController.addOnDestinationChangedListener(
object : NavController.OnDestinationChangedListener {
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?
) {
val view = weakReference.get()
if (view == null) {
navController.removeOnDestinationChangedListener(this)
return
}
view.menu.forEach { item ->
if (destination.matchDestination(item.itemId)) {
item.isChecked = …Run Code Online (Sandbox Code Playgroud) android android-jetpack android-architecture-navigation android-jetpack-navigation
根据下图,我的应用程序当前包含 2 个主要路由:Auth 和 Dashboard。当用户启动应用程序时,观察者将检查用户是否已登录并将他/她引导至登录页面/仪表板。
从图中你可以看到,我已经在 MainActivity 中设置了 NavGraph(包括所有仪表板路线)。问题是当用户单击底部导航项之一时,他/她将直接定向到视图页面。相反,我想要的是向用户展示仪表板支架内容内的视图。检查下图。
根据这些信息,一旦用户单击该项目,如何获取脚手架内容内的可组合视图?
任何指导将不胜感激。谢谢!
旁注:这是我的第一个撰写项目,我试图在不使用任何片段的情况下实现单活动架构。如果我的代码违反了任何设计或编程约定,我事先表示歉意,因为我对编程还很陌生[这里是自学者,又名业余爱好者:(]。
MainActivity.kt
package com.example.firstcomposeproject
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navigation
import com.example.firstcomposeproject.authview.FirebaseUserViewModel
import com.example.firstcomposeproject.authview.LogIn
import com.example.firstcomposeproject.authview.SignUpFragment
import com.example.firstcomposeproject.dashboard.*
import com.example.firstcomposeproject.ui.theme.FirstComposeProjectTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val userViewModel: FirebaseUserViewModel by viewModels()
setContent {
FirstComposeProjectTheme(
darkTheme = false
) {
val navController = rememberNavController()
/*
Observing FirebaseUser LiveData to check if …Run Code Online (Sandbox Code Playgroud) 所以我有两个选项卡,选项卡 A 和选项卡 B。每个选项卡都有自己的后堆栈。我使用此谷歌文档中的代码实现了多个返回堆栈导航
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the …Run Code Online (Sandbox Code Playgroud) android kotlin android-jetpack-navigation android-jetpack-compose jetpack-compose-navigation
我总共有 2 个屏幕,即SplashScreen和BottomNavScreen(带有底部导航)。
BottomNavScreen有 2 个屏幕,即Screen1和Screen2。
我想返回到SplashScreenfrom Screen2,但在导航到 时遇到此错误SplashScreen。
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.lool, PID: 8610
java.lang.IllegalStateException: ViewModelStore should be set before setGraph call
at androidx.navigation.NavController.setViewModelStore(NavController.kt:2164)
at androidx.navigation.NavHostController.setViewModelStore(NavHostController.kt:101)
at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:106)
at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:69)
at com.example.lool.NavigationKt.BottomNav(navigation.kt:34)
at com.example.lool.screens.Bottom_nav_screenKt$BottomNavScreen$2.invoke(bottom_nav_screen.kt:31)
at com.example.lool.screens.Bottom_nav_screenKt$BottomNavScreen$2.invoke(bottom_nav_screen.kt:30)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:316)
at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:314)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2$1$1.invoke(SubcomposeLayout.kt:241)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2$1$1.invoke(SubcomposeLayout.kt:241)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.ComposerKt.invokeComposable(Composer.kt:3330)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2577)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2573)
at …Run Code Online (Sandbox Code Playgroud) android kotlin android-jetpack-navigation android-jetpack-compose
我正在尝试使用 jetpack 撰写导航,但在理解嵌套导航和布局脚手架方面遇到了问题。
\n我有一个像这样的应用程序屏幕结构
\nroot\n\xe2\x94\x9c\xe2\x94\x80 A\n\xe2\x94\x9c\xe2\x94\x80 B <- Had a bottom navigation bar\n\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 C\n\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 D\nRun Code Online (Sandbox Code Playgroud)\n这是粗略的规格
\n我一直在浏览文档和 StackOverflow,据我所知,最好将脚手架放在 NavHost 之外。
\n但是,对于我的情况,如果我无法访问 A 和 B 内的脚手架,如何更新 A 和 B 中的应用程序栏?我只能想到在脚手架中进行分支,如下面的代码
\nScaffold(\n scaffoldState = scaffoldState,\n topBar = {\n when {\n currentDestination?.parent?.route == "Home" -> {\n TopAppBar(\n title = {\n Text("Home")\n },\n )\n }\n currentDestination?.route …Run Code Online (Sandbox Code Playgroud) 我正在尝试为完全用 Compose 编写的 Android 应用程序编写集成测试,该应用程序具有单个 Activity 并使用 Compose 导航来更改屏幕内容。
我设法正确交互并测试导航图显示的第一个屏幕,但是,一旦我导航到新目的地,测试就会失败,因为它不等待 NavHost 加载新内容。
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
@Test
fun appStartsWithoutCrashing() {
composeTestRule.apply {
// Check Switch
onNodeWithTag(FirstScreen.CONSENT_SWITCH)
.assertIsDisplayed()
.assertIsOff()
.performClick()
.assertIsOn()
// Click accept button
onNodeWithTag(FirstScreen.ACCEPT_BUTTON)
.assertIsDisplayed()
.performClick()
// Check we are inside the second screen
onNodeWithTag(SecondScreen.USERNAME_TEXT_FIELD)
.assertIsDisplayed()
}
}
}
Run Code Online (Sandbox Code Playgroud)
我确信这是一个计时问题,因为如果我在Thread.sleep(500)之前添加onNodeWithTag(SecondScreen.USERNAME_TEXT_FIELD).assertIsDisplayed(),测试就会成功。但我想Thread.sleep()在我的代码中避免使用 s 。
有没有更好的方法来告诉composeTestRuleNavHost 在执行之前等待 NavHost 加载新内容assertIsDisplayed()?
PS 我知道单独测试可组合项会更好,但我真的想使用 Espresso 模拟应用程序上的用户输入,而不仅仅是测试可组合项行为。
android-espresso android-jetpack-navigation android-jetpack-compose
我正在使用jetpack compose。我有两个屏幕,我想将位图从第一个屏幕发送到第二个屏幕。因此,我将位图转换为字符串并将其作为参数传递:
composable(
route = "${NavGraph.FilterScreen.route}/{screenShot}",
arguments = listOf(navArgument("screenShot") {
this.type = NavType.StringType
})
) {
FilterScreen(
innerPadding = padding,
navController = navController,
screenShot = it.arguments?.getString("screenShot")
)
}
Run Code Online (Sandbox Code Playgroud)
我从第一个屏幕导航到第二个屏幕,如下所示:
navController.navigate(NavGraph.FilterScreen.route + "/${bitmapToString(it)}")
Run Code Online (Sandbox Code Playgroud)
问题是:
看来是因为 Bitmap 的字符串版本太长,导航无法处理它并给出以下错误:
cannot be found in the navigation graph NavGraph(0x0) startDestination={Destination(0x78d845ec) route=home}
Run Code Online (Sandbox Code Playgroud)
我这么说是因为当我用包含位图值的字符串替换一个小的随机字符串时,一切都有效。
我也尝试过使用parcellable。但我收到错误,parcellable 不能有默认值,所以我们必须作为字符串传递。我该如何解决这个问题?