参数 onScreenChange : (String) -> Unit 如何传递到函数体:@Composable ((String) -> Unit) -> Unit?

Hel*_*oCW 5 kotlin android-jetpack-compose

代码A来自官方示例项目的主分支。

项目中有三个枚举类的子类Overview,Accounts和。BillsRallyScreen

类中有一个fun content(onScreenChange: (String) -> Unit) { body(onScreenChange) }接受参数的函数。onScreenChange : (String) -> UnitRallyScreen

虽然说法是enum class RallyScreen(val body: @Composable ((String) -> Unit) -> Unit) {..}body = { OverviewBody() }类中Overviewbody = { AccountsBody(UserData.accounts) } 类中 Accounts、类body = { BillsBody(UserData.bills) }Bills不需要传参(String) -> Unit),但是为什么Kotlin能运行良好,App启动时Tab导航也能很好呢fun content(onScreenChange: (String) -> Unit) { body(onScreenChange)}

代码A

@Composable
fun RallyApp() {
    RallyTheme {
        val allScreens = RallyScreen.values().toList()
        var currentScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) }
        Scaffold(
            topBar = {
                RallyTabRow(
                    allScreens = allScreens,
                    onTabSelected = { screen -> currentScreen = screen },
                    currentScreen = currentScreen
                )
            }
        ) { 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)
    } 
}


@Composable
fun OverviewBody(
    onClickSeeAllAccounts: () -> Unit = {},
    onClickSeeAllBills: () -> Unit = {},
    onAccountClick: (String) -> Unit = {},
) {
   ...
}

@Composable
fun AccountsBody(
    accounts: List<Account>,
    onAccountClick: (String) -> Unit = {},
) {
    ...
}

@Composable
fun BillsBody(bills: List<Bill>) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

新增内容:

加布里埃尔·皮萨罗:谢谢!

根据声明clas Overviewbody不需要任何参数,使用默认值,onScreenChange参数是如何传递的?

Overview(
   icon = Icons.Filled.PieChart,
   body = { OverviewBody() }  //It needn't any parameter
)


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

而且,当我添加两个默认参数以获取乐趣时,该应用程序可以正常运行OverviewBody(...)

@Composable
fun OverviewBody(
    onClickSeeAllAccounts: () -> Unit = {},
    onClickSeeAllBills: () -> Unit = {},
    a1: (String) -> Unit = {},              // I add
    onAccountClick: (String) -> Unit = {},
    a2: (String) -> Unit = {},              //I add
) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

Arp*_*kla 1

也许body = { OverviewBody() }应该body = { OverviewBody(onAccountClick = it) }Overview上课

事实上,不!

接收到的 lambdabody与 不同OverviewBody.onAccountClick。在bodylambda中,指的String的名称,您可以从此处RallyScreen的用法中看到。在lambda中,代表一个 accountName。onAccountClick String

通过类的声明Overviewbody不需要任何参数,它使用默认值,onScreenChange参数是如何传递的?

onScreenChange没有在任何地方调用,这就是导航不起作用的原因。

您可以SEE ALL使用以下方法使概览屏幕中的按钮正常工作:

Overview(
    icon = Icons.Filled.PieChart,
    body = { 
        OverviewBody(
            onClickSeeAllAccounts = { it(Accounts.name) },
            onClickSeeAllBills = { it(Bills.name) },
        ) 
    }
)
Run Code Online (Sandbox Code Playgroud)

onAccountClick中的 lambda处理OverviewBody起来很棘手。我们需要在单击AccountRow时显示SingleAccountBody可组合项。目前没有与该可组合项相对应的。因此,您必须创建一个新的枚举值,如下所示:RallyScreenRallyScreen

Overview(
    icon = Icons.Filled.PieChart,
    body = {
        OverviewBody(
            onClickSeeAllAccounts = { it(Accounts.name) },
            onClickSeeAllBills = { it(Bills.name) },
            onAccountClick = { accountName ->                   >>>>>>
                it(SingleAccount.name)                                |   
            }                                                         |
        )                                                             | 
    }                                                                 |
),                                                                    |
Accounts(                                                             |
    icon = Icons.Filled.AttachMoney,                                  | 
    body = {                                                          | 
        AccountsBody(UserData.accounts)                               |
    }                                                                 |
),                                                                    |
Bills(                                                                |
    icon = Icons.Filled.MoneyOff,                                     |
    body = { BillsBody(UserData.bills) }                              |
),                                                                    | 
SingleAccount(                                                        |
    icon = Icons.Filled.AttachMoney,                                  | 
    body = {                                                          |
        SingleAccountBody(UserData.getAccount(???accountName???)) <<<--
    }
);
Run Code Online (Sandbox Code Playgroud)

在这里,我们需要将从一个屏幕accountName接收到的信息发送到另一个屏幕以使导航正常工作。但在当前的代码结构中没有简单的方法可以做到这一点,我不想让这个答案变得太复杂。在此 Codelab 的最终代码中,已使用 NavArguments 传递此信息。OverviewSingleAccountaccountName