android - 当 2d 数组元素的变量在 Compose with Clean Architecture 中发生变化时重新组合

标签 android kotlin android-jetpack-compose clean-architecture

当二维数组中的对象中的至少一个变量发生变化时,我试图在板上实现快速图像重组。

我有一个二维数组实现为 Array<Array<Cell>> ,但是,为了实现重组,我创建了一个重复的 remember Screen 上的变量。这工作得足够快,但它导致了应用程序业务逻辑的代码重复,并且不允许我们使用所有功能,因此这不是正确的方法。

日期类的结构Cell :

data class Cell(
    val x: Int,
    val y: Int,
    var isOpen: Boolean = false,
    var index: Int = 0
)

Screen代码:

val row = 5
val col = 5
val board = viewModel.board.value

Column(Modifier.verticalScroll(rememberScrollState())) {
    Row(Modifier.horizontalScroll(rememberScrollState())) {
        Column {
            for (i in 0 until row) {
                Row {
                    for (j in 0 until col) {

                        val currentItem = remember(board) { mutableStateOf(board[i][j]) }  //duplicate remember variable

                        Image(
                            painter = painterResource(
                                id = if (currentItem.value.isOpen) {
                                    when (currentItem.value.index) {
                                        0 -> R.drawable.ic_num_0
                                        1 -> R.drawable.ic_num_1
                                        2 -> R.drawable.ic_num_3
                                        else -> R.drawable.ic_field
                                    }
                                } else {
                                    R.drawable.ic_field
                                }
                            ),
                            contentDescription = null,
                            modifier = Modifier.clickable {
                                currentItem.value = currentItem.value.copy(isOpen = true) // duplication of code
                                board[i][j].isOpen = true                                 // from the business logic of the application
                            }
                        )
                    }
                }
            }
        }
    }
}

ViewModel代码:

private val _board = mutableStateOf<Array<Array<Cell>>>(arrayOf())
val board: State<Array<Array<Cell>>> = _board

fun start() {
    _board.value = createBoard()// Use case that returns Array(row) { i -> Array(col) { j -> Cell(i, j) } }
}

之后,我添加了一个重复的二维数组 Array<Array<MutableState<Cell>>>ViewModel 。然而,为了更新它,我必须添加两个双循环 - 一个用于转换 Array<Array<Cell>>Array<Array<MutableState<Cell>>>另一个要转换Array<Array<MutableState<Cell>>>返回Array<Array<Cell>> 。 这有助于消除 Screen 上的重复代码。并允许我们实现所有功能,但可以预见的是大大增加了点击的处理时间。

Screen代码:

val row = 5
val col = 5
val board = viewModel.board.value

Column(Modifier.verticalScroll(rememberScrollState())) {
    Row(Modifier.horizontalScroll(rememberScrollState())) {
        Column {
            for (i in 0 until row) {
                Row {
                    for (j in 0 until col) {

                        val currentItem = remember(board) { viewModel.boardState[i][j] }

                        Image(
                            painter = painterResource(
                                id = if (currentItem.value.isOpen) {
                                    when (currentItem.value.index) {
                                        0 -> R.drawable.ic_num_0
                                        1 -> R.drawable.ic_num_1
                                        2 -> R.drawable.ic_num_3
                                        else -> R.drawable.ic_field
                                    }
                                } else {
                                    R.drawable.ic_field
                                }
                            ),
                            contentDescription = null,
                            modifier = Modifier.combinedClickable(
                                onClick = {
                                    viewModel.updateBoard(
                                        board[i][j],
                                        Event.OnClick
                                    )
                                }
                            )
                        )
                    }
                }
            }
        }
    }
}

ViewModel代码:

fun updateBoard(cell: Cell? = null, event: Event? = null) {
    val newBoard = boardState.map { row -> row.map { item -> item.value }.toTypedArray() }.toTypedArray() // one double loops
    _board.value = updateBoard(newBoard, cell, event) // Use case that returns Array(row) { i -> Array(col) { j -> Cell(i, j) } }
    boardState = board.value.map { row -> row.map { item -> mutableStateOf(item) }.toTypedArray() }.toTypedArray() // two double loops
}

问题:在更改二维数组中任何对象的变量时如何实现快速重组?

注意:在我的项目中,我遵循整洁架构的原则,所以我不能使用 Array<Array<MutableState<Cell>>>在域级别,因为 MutableStateandroidx 的一部分图书馆。

此外,我想补充一点,当您单击二维字段单元格时,其他单元格可能会发生变化,而不仅仅是被单击的单元格。

也许可以使用 MutableStateFlow 来解决这个问题然而,与 MutableState 不同,它不会导致元素被重组。也许还有其他我不知道的方法。

更新:我尝试转换 Array<Array<Cell>>Array<Array<MutableStateFlow<Cell>>> ,并转换StateFlow撰写StateScreen使用collectAsState() ,但这会导致错误。

Screen :

val board by viewModel.board

Column(Modifier.verticalScroll(rememberScrollState())) {
    Row(Modifier.horizontalScroll(rememberScrollState())) {
        Column {
            for (i in 0 until row) {
                Row {
                    for (j in 0 until col) {

                        val currentItem = remember(board) { board[i][j].collectAsState() } // error
                            ...

ViewModel :

private val _board = mutableStateOf<Array<Array<MutableStateFlow<Cell>>>>(arrayOf())
val board: State<Array<Array<MutableStateFlow<Cell>>>> = _board

fun updateBoard(cell: Cell? = null, event:Event? = null) {
    _board.value = updateBoard(board.value, cell, event)
}

最佳答案

当您读取状态时会发生重组。所以我认为最整洁的方法是为每个单元格使用可变状态列表,并且只在单元格中读取。请注意,如果您内联 Cell,它会导致整个集合重组,因为 RowColumn 是内联函数,会阻止 Compose 进行智能重组。

@Composable
fun Test() {
    val m = 10
    val n = 10
    val state = remember {
        List(m) {
            List(n) { mutableStateOf(0) }
        }
    }
    val coroutineScope = rememberCoroutineScope()
    Column {
        for (i in 0 until m)
            Row {
                for (j in 0 until n)
                    Cell(
                        state = state[i][j],
                        onClick = {
                            state[i][j].value += 1
                        },
                    )
            }
    }
}

@Composable
private fun Cell(
    state: State<Int>,
    onClick: () -> Unit,
) {
    Text(
        state.value.toString(),
        modifier = Modifier
            .clickable(onClick = onClick)
            .padding(10.dp)
    )
}

如果由于某种原因您需要在数据层上执行此操作,并且出于某些架构原因想要使用不可变状态,您可以对流使用相同的方法 - 您只需要有一个可以单独为每个值收集它的点。

@Composable
fun Test() {
    val m = 10
    val n = 10
    val state = remember {
        MutableStateFlow(
            List(m) {
                List(n) { 0 }
            }
        )
    }
    val coroutineScope = rememberCoroutineScope()
    Column {
        for (i in 0 until m)
            Row {
                for (j in 0 until n)
                    Cell(
                        valueStateFlow = remember {
                            state.map { it[i][j] }.stateIn(coroutineScope, SharingStarted.Eagerly, state.value[i][j])
                        },
                        onClick = {
                            state.value = state.value.toMutableList().also {column->
                                column[i] = column[i].toMutableList().also { row ->
                                    row[j] = row[j] + 1
                                }.toImmutableList()
                            }.toImmutableList()
                        },
                    )
            }
    }
}

@Composable
private fun Cell(
    valueStateFlow: StateFlow<Int>,
    onClick: () -> Unit,
) {
    val value by valueStateFlow.collectAsState()
    Text(
        value.toString(),
        modifier = Modifier
            .clickable(onClick = onClick)
            .padding(10.dp)
    )
}

关于android - 当 2d 数组元素的变量在 Compose with Clean Architecture 中发生变化时重新组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76177971/

相关文章:

unit-testing - MockK - 在顶级 val 上调用模拟/ spy 顶级扩展方法

kotlin - 为什么可组合对象看似无状态(唯一传递的参数是函数,而不是状态),但会进行重组

android - Jetpack Compose 是否有创建自定义过度滚动效果的工具?

android - 如何在 Jetpack Compose 中实现 sp 和 % 等动态测量?

java - "Could not find mopub-android-sdk.apk!"

java - RxJava Single.just 在主线程中

gradle - Kotlin Gradle脚本设置

android - 尝试使用 Android material3 时出现 Unresolved reference 错误

android - iOS/Android - 通过 SDK 禁用移动数据(蜂窝数据)

android - 找不到与com.google.android.gms:play-services-base:[15.0.1,16.0.0)匹配的版本