【问题标题】:key { ... } composable vs LazyList's item key as composable keykey { ... } 可组合 vs LazyList 的项目键作为可组合键
【发布时间】:2022-10-15 17:10:17
【问题描述】:

我有一个使用LazyColumn 列出的待办事项列表,每个项目都是处理每个 TodoModel 的另一个可组合 (CardView)。我在用着记住可保存管理此 CardView 自己的可组合(例如 TextField、可点击图像等)的状态,以便在方向更改时也能存活。

我已经学会了使用LazyList 的密钥来告诉结构每个项目都是唯一的,在这种情况下我使用的是模型的 id。

这个 todolist 的工作方式是它的单个屏幕显示已保存的 todo 列表(通过房间持久化保存(它将显示在屏幕的待办事项顶部),这是用户可以编辑待办事项的标题和内容的地方。总而言之,您可以在单个屏幕中删除/编辑/创建新的 Todo,所有保存的 Todo 都有正 ID,对于新的,我总是将其设置为 -1。保存后,新 todo 模型的 id 为 -1L 将在整个屏幕的视图模型(DashboardScreen 的ViewModel)中提升的列表(由 Room 返回的 id)中修改

我注意到的是当我以这种方式在惰性列表中定义一个键时(LazyDSL)

 LazyColumn {
    items(key = { it.id },items = todoList) { todo ->
            TodoCardItem(
                todoModel = todo,
                ...,
                ...

并通过以下一系列动作

  • 点击Fab,新建todo(id of -1L),空标题
  • 保存这个新的Todo(保存后,id会被修改为正数),title = "My First Todo"
  • 点击Fab,新建Todo(-1L的id),title不为空,显示=“My First Todo”

它引用了卡的旧状态。

但是当我以这种方式定义键时(可组合键)

LazyColumn {
    items(items = todoList) { todo ->
        key (todo.id) {
            TodoCardItem(
                todoModel = todo, 
                ...,
                ...

它按我的预期工作,每一个新的待办事项都是完全干净的,它也能在旋转等配置更改中幸存下来(尽管当我最小化应用程序时我崩溃了,这是我需要稍后处理的事情)。

对于rememberSaveable,它被初始化为TodoModel

.....
companion object {
    val Saver = Saver<TodoItemState, Map<String, Any>>(
        save = { instanceOfOriginal ->
            mapOf(
                KEY_MODEL to instanceOfOriginal.todoModel,
                KEY_TITLE to instanceOfOriginal.title
            )
        },
        restore = { restorer ->
            val state = TodoItemState(restorer[KEY_MODEL] as TodoModel)
            state.title = restorer[KEY_TITLE] as String
            state
        }
    )

    private const val KEY_MODEL = "key_model"
    private const val KEY_TITLE = "key_title"
   }
 }

@Composable
 internal fun rememberTodoItemState(todoModel: TodoModel) = 
 rememberSaveable(
        saver = TodoItemState.Saver
 ) {
     TodoItemState(todoModel)
 }

这是一个很大的代码,所以如果对它的某些部分有进一步的担忧,比如显示视图模型代码,模型的 id 是如何修改的,我会在进一步的问题上粘贴它。

【问题讨论】:

    标签: kotlin android-jetpack-compose lazycolumn


    【解决方案1】:

    通过做这个:

    items(items = todoList) { todo ->
        key (todo.id) {
    

    您不仅要重置 -1L 视图,还要重置插入的单元格下方的所有其他单元格。

    当你没有指定key时,默认情况下它等于单元格索引,所以在另一个里面有两个键,当索引移动时,所有内部键都变得无效并且必须创建一个新视图.您可以通过在key 中添加带有日志的LaunchedEffect 来检查此行为。

    我认为最正确的方法是为每个新项目创建一个唯一键,并存储一个带有新项目 ID 的地图:这将确保 tmp 单元格与新内容一起重用。

    private val newItemIdsMap = mutableMapOf<Long, Long>()
    
    fun addTmpItem() {
        var tmpId: Long
        do {
            tmpId = Random.nextLong(Long.MIN_VALUE, -1)
        } while (newItemIdsMap.containsValue(tmpId))
        val tmpItem = Item(id = tmpId)
        items.add(0, tmpItem)
    }
    
    fun saveItem(tmpItem: Item) {
        val savedItem = saveToDb(tmpItem)
        newItemIdsMap[savedItem.id] = tmpItem.id
        items[0] = savedItem
    }
    
    fun getItemId(item: Item) : Long {
        val savedId = newItemIdsMap[item.id]
        return savedId ?: item.id
    }
    

    【讨论】:

    • 所以可组合键并不完全是列表内部的唯一标识符,它是一个唯一的代码块。再次感谢@Pylyp
    • @zab.y 是的,它是一个单独的组件,可以在没有惰性视图的情况下使用,例如解决this one之类的问题
    猜你喜欢
    • 2012-05-25
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 2016-11-02
    • 2022-08-23
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    相关资源
    最近更新 更多