【问题标题】:How is the paramter onScreenChange : (String) -> Unit passed to the function body : @Composable ((String) -> Unit) -> Unit?Screen Change : (String) -> Unit 上的参数如何传递给函数体:@Composable ((String) -> Unit) -> Unit?
【发布时间】:2021-12-28 06:07:26
【问题描述】:

代码A来自官方样本project的主分支。

项目中有枚举类RallyScreen的三个子类OverviewAccountsBills

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

虽然语句是enum class RallyScreen(val body: @Composable ((String) -> Unit) -> Unit) {..},但Overview类中的body = { OverviewBody() }Accounts类中的body = { AccountsBody(UserData.accounts) }Bills类中的body = { BillsBody(UserData.bills) }不需要通过参数(String) -> Unit),为什么App启动时Kotlin可以运行良好,通过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>) {
    ...
}

添加内容:

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

clas Overview的说法,body不需要任何参数,它使用默认值,onScreenChange参数是如何传递的?

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


@Composable
fun content(onScreenChange: (String) -> Unit) {
   body(onScreenChange)
} 

此外,当我为乐趣添加两个默认参数OverviewBody(...) 时,该应用程序可以正常运行。

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

【问题讨论】:

    标签: kotlin android-jetpack-compose


    【解决方案1】:

    也许body = { OverviewBody() } 应该是Overview 类中的body = { OverviewBody(onAccountClick = it) }

    其实,不!!!

    body 接收到的 lambda 与 OverviewBody.onAccountClick 不同。在bodylambda 中,String 指的是RallyScreen 的名称,从它的用法here 可以看出。而在onAccountClicklambda 中,String 代表一个帐户名。

    通过Overview类的说法,body不需要任何参数,它使用默认值,onScreenChange参数是如何传递的?

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

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

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

    OverviewBody 中的 onAccountClick lambda 很难处理。当单击AccountRow 时,我们需要显示SingleAccountBody 可组合。现在没有对应于该组合的RallyScreen。因此,您必须在 RallyScreen 中创建一个新的枚举值,如下所示:

    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???)) <<<--
        }
    );
    

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

    【讨论】:

    • 谢谢!但是"onScreenChange is not passed called anywhere and that's why navigation doesn't work." 是错误的,当我点击一个标签图标时,导航可以与项目的主分支很好地配合。
    • 我的意思是当您点击 AccountRowSEE ALL 按钮时,导航不起作用。由于this 行,选项卡更改时的屏幕更改。 onScreenChange lambda 是传递给屏幕 body 的内容,以允许导航以响应来自屏幕内部且未被调用的事件。
    • @HelloCW 这有意义吗?
    【解决方案2】:

    请记住,每个on 变量都是一个回调。 onScreenChange 将变量 it 隐式传递为字符串,但不必在回调中使用变量。

    1. it 是隐式传递的,但是在您提供的 GitHub 链接中,程序员决定将 it 重命名为 onScreenChange。然后将该回调传递给OverviewBody,它会覆盖默认的{},如您所说。我不明白你的问题是onAccountClick 来自哪里。根据GitHub代码,那不是OverviewBody的参数。

    2. content 有一个用于另一个回调的参数,你注意到了。然后调用枚举中每个类的body 变量,并将回调参数传递给调用。将来,该回调将仅由OverviewBody 使用。请记住,body(callback)body.invoke(callback) 相同。

    我希望这会有所帮助。如果需要更多解释,请告诉我。

    进一步参考:

    其他内容编辑:

    1. 我在您的代码中看到了参数,而不是 GitHub 上的参数。我很抱歉错过了这一点。
    2. body 是一个变量函数。这意味着可以通过编写body(...)body.invoke(...) 来调用变量函数。隐式传递的 String 参数 it 将被忽略并使用默认值,除非按照您的建议 (onAccountClick=it) 指定。

    【讨论】:

    • 谢谢!请您看看我在问题中添加的内容吗?
    • @HelloCW 我通过编辑我的答案扩展了您的问题。基本上,body 没有使用onScreenChange,因为{ OverviewBody() } 没有使用it。如果您将body 更改为{ OverviewBody(onAccountClick=it) },则onScreenChange 将作为参数it 传递。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-12
    • 2021-08-23
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多