在事件中使用 Context/Toast 时出现不需要的重组 - Jetpack Compose

2024-01-14

在 Jetpack Compose 应用程序中,我有两个与此处类似的可组合项:

@Composable
fun Main() {
    println("Composed Main")
    val context = LocalContext.current

    var text by remember { mutableStateOf("") }

    fun update(num: Number) {
        text = num.toString()
        Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
    }

    Column {
        Text(text)
        Keypad { update(it) }
    }
}

@Composable
fun Keypad(onClick: (Number) -> Unit) {
    println("Composed Keypad")

    Column {
        for (i in 1..10) {
            Button(onClick = {onClick(i)}) {
                Text(i.toString())
            }
        }
    }
}

单击每个按钮都会导致两个可组合项重新组合并生成以下输出:

I/System.out: Composed Main
I/System.out: Composed Keypad

重构Keypad不需要可组合性,它会使应用程序冻结(在较大的项目中冻结几秒钟)。

删除事件句柄中上下文的使用(在这里,注释掉Toast)解决了问题并且不重新组合Keypad并产生以下输出:

I/System.out: Composed Main

有没有其他方法可以在事件中使用上下文而不会导致不必要的重组?


问题是Context not成为一个stable https://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8 (@Stable) 类型。的 lambda/回调KeyPad正在更新一个状态,紧接着是一个使用不稳定状态的组件Context,这导致onClickLambda to be 重新创造 https://stackoverflow.com/questions/72633554/why-does-a-call-to-a-logic-method-on-a-viewmodel-with-stateflow-as-a-property-ca(你可以看到它的hashcode每次单击按钮时都会更改),从而使Keypad可组合的not 可跳过 https://chris.banes.me/posts/composable-metrics/.

您可以考虑四种方法来处理您的问题。我还对代码进行了一些更改,删除了本地函数,并将所有内容直接放入 lambda/回调中,以使所有内容更小。

对于前两个,首先创建一个像这样的通用包装类。

@Stable
data class StableWrapper<T>(val value: T)

将上下文包装在 @Stable 包装器中

使用通用包装类,您可以考虑包装上下文并像这样使用它

@Composable
fun Main() {
    Log.e("Composable", "Composed Main")

    var text by remember { mutableStateOf("") }

    val context = LocalContext.current
    val contextStableWrapper = StableWrapper(context)

    Column {
        Text(text)
        Keypad {
            text = it.toString()
            Toast.makeText(contextStableWrapper.value, "Toast", Toast.LENGTH_SHORT).show()
        }
    }
}

将 Toast 包裹在 @Stable 包装器中

Toast也是一种不稳定的类型,所以你必须用第二种方法使其“稳定”。

请注意,这仅适用于您的 Toast 消息不会更改的情况。

将它们举到你的上方Main您将在其中创建静态消息 Toast 的实例并将其放入稳定的包装器中

val toastWrapper = StableWrapper(
    Toast.makeText(LocalContext.current, "Toast", Toast.LENGTH_SHORT)
)

Main(toastWrapper = toastWrapper)

你的主要可组合项将如下所示

@Composable
fun Main(toastWrapper: StableWrapper<Toast>) {
    Log.e("Composable", "Composed Main")

    var text by remember { mutableStateOf("") }

    Column {
        Text(text)
        Keypad {
            text = it.toString()
            toastWrapper.value.show()
        }
    }
}

remember{…}上下文

(我可能期望在这里进行一些修正),我认为这被称为“记忆值(Context) 里面remember{…}“,这看起来类似于延迟读取 https://medium.com/androiddevelopers/jetpack-compose-debugging-recomposition-bfcf4a6f8d37.

@Composable
fun Main() {
    Log.e("Composable", "Composed Main")

    var text by remember { mutableStateOf("") }
    val context = LocalContext.current
    val rememberedContext = remember { { context } }

    Column {
        Text(text)
        Keypad {
            text = it.toString()
            Toast.makeText(rememberedContext(), "Toast", Toast.LENGTH_SHORT).show()
        }
    }
}

使用副作用

您可以利用撰写副作用 https://developer.android.com/jetpack/compose/side-effects然后把吐司放进去。

Here, SideEffect将执行每个重组后。

SideEffect {
   if (text.isNotEmpty()) {
       Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
   }
}

或者你可以利用LaunchedEffect使用text作为它的关键,所以在成功的重新组合时,当text与之前的值不同(无效)的变化,LaunchedEffect将重新执行并再次显示 toast

LaunchedEffect(key1 = text) {
   if (text.isNotEmpty()) {
       Toast.makeText(context, "Toast", Toast.LENGTH_SHORT).show()
   }
}

将打印替换为日志语句,这是单击按钮时任何方法的输出

E/Composable: Composed Main   // first launch of screen
E/Composable: Composed Keypad // first launch of screen

// succeeding clicks

E/Composable: Composed Main
E/Composable: Composed Main
E/Composable: Composed Main
E/Composable: Composed Main

我唯一不确定的部分是第一种方法,即使 Toast 不是基于第二种方法的稳定类型,只需在第一种方法中将上下文包装在稳定包装器中就足以满足Keypad可组合以被跳过。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在事件中使用 Context/Toast 时出现不需要的重组 - Jetpack Compose 的相关文章

  • 在我的 Android 应用程序中使用 ServerValue.TIMESTAMP

    我读过很多相关的 stackoverflow 问题 ServerValue TIMESTAMP 但我不知道如何在我的应用程序中使用它 我需要获取帖子创建的时间戳 时间戳应该添加到与帖子的 uid 作者等相同的位置 代码片段其中写这篇文章Fi
  • 如何从该 JAVA 文件中提取 Delphi 类以与 Android 一起使用?

    我的Delphi XE7项目需要与FTDI FT311 Android 配件芯片 http www ftdichip com Products ICs FT311D html 他们帮助提供了一个 Android 演示 其中包括他们的 JAV
  • 我在哪里可以获得可靠的熵来源(真正的随机性字节[])?

    目前 我正在寻找一种方法来增加随机性的质量 in my Android应用程序 纸牌游戏 之前 估计对于我的情况 52 排列 至少需要 226 位熵 226 个随机位 我打算用这个byte 作为种子SecureRandom SecureRa
  • 使用一个 apk 安装两个应用程序

    我有 2 个应用程序 1 内容提供者 2 使用此 ContentProvider 的应用程序 我需要使用单个 apk 文件安装这 2 个应用程序 我想在 Eclipse 中同时推送这两个应用程序 如果我将另一个项目添加到一个应用程序的构建路
  • 导入已经创建的sqlite数据库(xamarin)

    我正在使用 Xamarin 想知道如何导入我已经创建的 sqlite 数据库 到目前为止 我已将其添加到资产文件夹中 但不知道下一步从哪里开始 string localPath Path Combine System Environment
  • API29 上不推荐使用 setColorFilter

    我使用以下行来更改 VectorDrawable 的颜色 mydrawable getBackground setColorFilter color PorterDuff Mode SRC ATOP 这很好用 尽管它现在已被弃用 文档建议我
  • Android:我可以创建一个不是矩形的视图/画布吗?圆形的?

    我有一个圆形视图 悬停在主要内容上方 gt 从屏幕出来的 z 轴方向 当有人点击屏幕时 我希望选择主要内容或悬停在上方的视图 当它覆盖主视图时 到目前为止效果很好 我在透明画布上有一个圆形物品 这意味着您可以看到该圆圈之外的背景的所有内容
  • android中根据屏幕尺寸计算图像尺寸

    我正在尝试根据屏幕尺寸计算图像高度和宽度 我从后端获取 5 x 7 尺寸的图像 为了将像素乘以 72 进行转换 我有 360 X 504 尺寸的图像 对于 360 X 504 我的动态透明矩形区域将显示为 1 223 x 1 179 即 8
  • 更改卡片高度即更改 Jetpack 中与 Material 3 组合的卡片颜色

    我正在使用 Card 可组合项 我希望它的颜色为白色 但是当我向它添加一些高度时 它的颜色会更改为更像主要容器颜色 我看过文档 其中有一种称为高程覆盖的东西 但找不到说明如何使用它的示例 这是我的代码 Card modifier Modif
  • 具有自定义源集的 Android Gradle 风格 - gradle 文件应该是什么样子?

    我有一个旧的 eclipse 项目 我已经转移到 android studio 并设置为使用flavor 它似乎工作得很好 直到我开始尝试在我的风格之间使用不同的 java 文件 我的项目设置是这样的 ProjectRoot acitonb
  • Android:使 Dialog 周围的所有内容都比默认值更暗

    我有一个具有以下样式的自定义对话框 它显示了一个无边框对话框 后面的任何内容都会 稍微 变暗 我的设计师希望背后的一切都比 Android 的默认设置更暗 但不是完全黑色 有这样的设置吗 我能想到的唯一解决方法是使用全屏活动而不是对话框 只
  • 如何在 NumberPicker 中一次显示 3 个以上的值

    我正在创建一个数字选择器 如下图所示 但如果有可用空间 我想显示 3 个以上的值 该选择器有 20 个项目 并且有足够的空间来显示 3 个以上的值 这可以使用 NumberPicker 来完成吗 只需以编程方式设置numberPicker
  • 使用 Android Studio 进行调试永远停留在“等待调试器”状态

    UPDATE The supposed重复是一个关于陷入 等待调试器 执行时Run 而这个问题就陷入了 等待调试器 执行时Debug 产生问题的步骤不同 解决方案也不同 每当我尝试使用Android Studio的调试功能时 运行状态总是停
  • Android 中的列表视图分页

    我有一个列表视图 其中显示了 50 个元素 我决定对视图进行分页 以便视图的每个部分都有 10 个元素 然后单击 下一个 按钮以获取下一个 10 个元素 如何设置10个数据 我关注这篇文章http rakhi577 wordpress co
  • 如何构建自定义摄像机应用程序?

    我正在尝试开发一个自定义摄像机录像机 当我的设备在 Activity 的 beginRecording 中执行 start MediaRecorder 方法时 应用程序崩溃 我不知道出了什么问题 因为我遵循谷歌API指南 http deve
  • 在android中创建SQLite数据库

    我想在我的应用程序中创建一个 SQLite 数据库 其中包含三个表 我将向表中添加数据并稍后使用它们 但我喜欢保留数据库 就好像第一次安装应用程序时它会检查数据库是否存在 如果存在则更新它 否则如果不存在则创建一个新数据库 此外 我正在制作
  • Flash 对象未显示在phonegap android 中

    我已经在 android 手机间隙创建了一个应用程序 我有一个屏幕 我想显示一个静态 flash obj 所以我在屏幕 HTML 页面中放入了以下代码
  • 如何正确编写AttributeSet的XML?

    我想创建一个面板适用于 Android 平台的其他小部件 http code google com p android misc widgets 在运行时 XmlPullParser parser getResources getXml R
  • R.java是手动修改的!恢复到生成的版本

    我在布局中添加了一个 xml 文件 之后这个错误就来了 但问题是我还没有接触过 R java 文件 现在 在我的新活动中 我要将其内容视图设置为我新创建的 xml 文件 但是当我执行 R layout 时 新创建的 xml 不会出现在建议中
  • 将焦距(以毫米为单位)转换为像素 - Android

    在 Android 中 我当前正在访问camera s焦距通过使用getFocalLength in Camera1 Camera2不是一个选择 我正在尝试完全填充当前的计算 focal length pix focal length m

随机推荐