Jetpack Compose Navigation 的导航设计是否良好?

Hel*_*oCW 8 kotlin android-jetpack-navigation android-jetpack-compose

以下代码来自官方示例项目

有两个分支,mainend

我发现代码主体代码结尾使用不同的方式进行导航。

代码 main简单明了,在其他项目中,它基于 State 进行导航,就像来自项目的代码 A一样

代码端用来导航,但是我们使用Jetpack Compose的时候NavHostController好像就不用再用了吧?Navigation

代码主要

@Composable
fun RallyApp() {
    RallyTheme {
        val allScreens = RallyScreen.values().toList()
        var currentScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) }
        Scaffold(
          ...
        ) { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                currentScreen.content(
                    onScreenChange = { screen ->
                        currentScreen = RallyScreen.valueOf(screen)
                    }
                )
            }
        }
    }
}

enum class RallyScreen(
    val icon: ImageVector,
    val body: @Composable ((String) -> Unit) -> Unit
) {
    Overview(
        icon = Icons.Filled.PieChart,
        body = { OverviewBody() }
    ),
    Accounts(
        icon = Icons.Filled.AttachMoney,
        body = { AccountsBody(UserData.accounts) }
    ),
    Bills(
        icon = Icons.Filled.MoneyOff,
        body = { BillsBody(UserData.bills) }
    );

    @Composable
    fun content(onScreenChange: (String) -> Unit) {
        body(onScreenChange)
    }
}
Run Code Online (Sandbox Code Playgroud)

代码结束

@Composable
fun RallyNavHost(navController: NavHostController, modifier: Modifier = Modifier) {
    NavHost(
        navController = navController,
        startDestination = Overview.name,
        modifier = modifier
    ) {
        composable(Overview.name) {
            OverviewBody(
              ...
            )
        }
        composable(Accounts.name) {
          ...
        }
        composable(Bills.name) {
          ...
        }       
    }
}



enum class RallyScreen(
    val icon: ImageVector,
) {
    Overview(
        icon = Icons.Filled.PieChart,
    ),
    Accounts(
        icon = Icons.Filled.AttachMoney,
    ),
    Bills(
        icon = Icons.Filled.MoneyOff,
    );

    companion object {
        fun fromRoute(route: String?): RallyScreen =
            when (route?.substringBefore("/")) {
                Accounts.name -> Accounts
                Bills.name -> Bills
                Overview.name -> Overview
                null -> Overview
                else -> throw IllegalArgumentException("Route $route is not recognized.")
            }
    }
Run Code Online (Sandbox Code Playgroud)

代码A

fun CraneHomeContent(
   ...
) {
    val suggestedDestinations by viewModel.suggestedDestinations.collectAsState()

    val onPeopleChanged: (Int) -> Unit = { viewModel.updatePeople(it) }
    var tabSelected by remember { mutableStateOf(CraneScreen.Fly) }

    BackdropScaffold(
        ...
        frontLayerContent = {
            when (tabSelected) {
                CraneScreen.Fly -> {
                  ...
                }
                CraneScreen.Sleep -> {
                   ...
                }
                CraneScreen.Eat -> {
                   ...
                }
            }
        }
    )
}
Run Code Online (Sandbox Code Playgroud)

And*_*Dev 8

我从早期的 alpha 阶段就开始使用 Compose,但很快我就对 Google 在提供更现代的单一活动应用程序导航方法方面的蹩脚尝试感到失望。当您考虑到 Android 基于视图的系统完全被 Compose 使用的声明方法所取代时,您必须认真思考为什么他们会坚持使用不允许您将对象从一个屏幕传递到另一个屏幕的导航控制器。还有一个问题是,从一个屏幕转换到另一个屏幕时添加动画是事后才想到的。有一个支持动画过渡的附加组件。

但也许 Compose 最糟糕的事情是它无法处理设备配置更改。在旧的基于视图的系统下,您在 xml 文件中定义布局,并将它们放置在资源文件夹中,这些文件夹名称中包含限定符,这将帮助 Android 根据屏幕密度、方向、屏幕尺寸等选择正确的布局.Compose 已经不再适用了。最终,谷歌确实添加了 API 来处理需要根据屏幕尺寸和密度进行选择的可组合项。但最终,您最终会在可组合项中编写此决策逻辑,并且您的代码开始看起来像意大利面条。当 Google 的 Android 团队选择将 UI 布局与决定选择哪个布局的逻辑混合在一起时,他们完全忘记了最基本的“关注点分离”。设计可组合项是一回事,如何选择它们又是另一回事。不要把两者混为一谈。当您集成这些 API 时,您的可组合项的可重用性会变得越来越差。Android 的原始开发人员(不是来自 Google)非常清楚如何让操作系统根据设备配置的更改来管理布局选择。

我选择创建自己的框架,以与基于视图的系统几乎相同的方式处理导航和管理可组合项。它还解决了无法在屏幕之间传递对象的问题。如果您有兴趣,可以在以下网址查看:

https://github.com/JohannBlake/Jetmagic

因此,要回答您有关 Compose Navigation 是否适合导航的问题,我不得不说不。我曾经使用过它,发现它严重缺乏。如果您想成为普通的 Android 开发人员,那么请使用 Compose Navigation。但如果您想规划自己的道路并将自己从 Google 糟糕的设计实践中解放出来,那么请使用 Jetmagic,甚至更好,创建您自己的导航框架。这并不难。


F.M*_*sir 3

回答你的问题:Code Main这不是正确的导航方式。

这是一个入门示例。在这种情况下,您只需隐藏和显示可组合项即可。

为什么使用导航更好?

  1. 生命周期得到了更好的处理。想象一下,您想要启动一个cameraX 可兼容屏幕,然后想要返回到最初的可组合屏幕。导航组件将处理和释放资源。

  2. 可组合项被添加到返回堆栈中。因此,当您按返回时,它会自动返回到上一个可组合屏幕。

  3. 您会得到这个漂亮的动画,并且不会立即看到下一个屏幕。

我确信还有其他要点,但这些是我现在想到的……