繁体   English   中英

Android Livedata 使用对象的副本更新他的状态

[英]Android Livedata updates his states with copy of objects

嗨,我想我现在真的不知道实时数据是如何工作的。

我有一个带有预填充值的二维数组。

val randomboard =
    arrayOf(arrayOf(0, 0, 0, 0), arrayOf(0, 0, 0, 0), arrayOf(0, 0, 0, 0), arrayOf(0, 0, 0, 0))

还有一个实时数据 Object,它只在我的 ViewModel 的初始化 Function 发布一次我的阵列的当前 State。

init {
    _preFillButtons.postValue(randomboard.copyOf())
}

所以我希望只有我的数组的当前 State 和没有未来的状态被推送到我的 LiveData 对象所以我给了它我的数组的副本。

当我改变我的阵列随机板的任何值然后像我的方向一样改变时,我的 livedata object 在他的 state 中只有这个新值,没有我将任何新的 State 推到那个 LiveData object。

也许 Livedata object 不是我需要的,但我不知道为什么连副本 object 都更新了。

更新:

完整代码

class GameActivity : AppCompatActivity(), View.OnClickListener {

val viewModel: GameViewModel by viewModels()

val buttonIDs = arrayOf(
    intArrayOf(R.id.button1, R.id.button2, R.id.button3, R.id.button4),
    intArrayOf(R.id.button5, R.id.button6, R.id.button7, R.id.button8),
    intArrayOf(R.id.button9, R.id.button10, R.id.button11, R.id.button12),
    intArrayOf(R.id.button13, R.id.button14, R.id.button15, R.id.button16)
)

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    viewModel.preFillButtons.observe(this) {
        prefillButtons(it)
    }
}

private fun prefillButtons(board: Array<Array<Int>>) {
    for (row in 0..3) {
        for (col in 0..3) {
            val button = findViewById<Button>(buttonIDs[row][col])
            button.setTag(R.id.row, row)
            button.setTag(R.id.column, col)
            button.setOnClickListener(this)
            if (board[row][col] != 0) {
                button.text = board[row][col].toString()
                button.isEnabled = false
            } else {
                button.text = getString(R.string.defaultbuttontext)
                button.isEnabled = true
            }
        }
    }
}

override fun onClick(view: View?) {
    val button = view as? Button
    if (button != null) {
        val row = button.getTag(R.id.row) as Int
        val col = button.getTag(R.id.column) as Int
        showAlertDialog(col, row)
    }
}

private fun showAlertDialog(col: Int, row: Int) {
    val builder = AlertDialog.Builder(this)
    builder.setItems(R.array.choices) { dialogInterface: DialogInterface, i: Int ->
        if (i != 4) {
            viewModel.trySetValue(row, col, i + 1)
        }
    }
    builder.setTitle(getString(R.string.dialogtitle, row + 1, col + 1))
    builder.show()
}
}

视图模型:

class GameViewModel : ViewModel() {

val preFillButtons: LiveData<Array<Array<Int>>>
    get() = _preFillButtons
private val _preFillButtons = MutableLiveData<Array<Array<Int>>>()

val randomboard =
    arrayOf(arrayOf(0, 0, 0, 0), arrayOf(0, 0, 0, 0), arrayOf(0, 0, 0, 0), arrayOf(0, 0, 0, 0))

init {
    _preFillButtons.postValue(randomboard.copyOf())
}

fun trySetValue(row: Int, col: Int, num: Int) {
    randomboard[0][0] = 1
}
}

屏幕旋转后(配置更改后)看到新的 state 与 LiveData 的工作方式无关。 您正在正确观察您的 LiveData。

所以我希望只有我的数组的当前 State 和没有未来的状态被推送到我的 LiveData 对象所以我给了它我的数组的副本。

这里稍作更正,您给了它一个数组的浅表副本。 浅拷贝是原始数组的副本,但如果项目是实例(对对象的引用),它们将通过引用进行复制。 这意味着对数组副本中项目的更改也会影响原始数组中的项目,因为它是相同的引用(=相同的实例)。

因为你有一个 arrays 的数组,你的数组中的项目是 4 个其他 arrays 的实例。当你在randomboard中更改trySetValue内的值时,你每次都会访问相同的 4 个数组实例。

arrayOf调用创建一个新的数组实例并用作为参数提供的值填充它。 因此,您对randomboard的初始分配会创建 5 个数组实例:

  • 4 是 Integer[] 类型,包含值 0、0、0、0
  • 1 是 Integer[][] 类型,包含对上面 4 arrays 的引用。 对该数组的引用存储在字段randomboards

这段代码通过引用比较值,显示主数组是一个新实例(因为调用了copyOf ),但里面的 arrays 是相同的实例。

init {
    val copy = randomboard.copyOf()
    Log.d("Test", "copy === randomboard: ${copy === randomboard}") // false
    Log.d("Test", "copy[0] === randomboard[0]: ${copy[0] === randomboard[0]}") // true
    Log.d("Test", "copy[1] === randomboard[1]: ${copy[1] === randomboard[1]}") // true
    Log.d("Test", "copy[2] === randomboard[2]: ${copy[2] === randomboard[2]}") // true
    Log.d("Test", "copy[3] === randomboard[3]: ${copy[3] === randomboard[3]}") // true
    //...
}

如果你不想改变影响数组的副本,你必须做一个深拷贝 您可以通过在源数组上调用.map或使用Array<T>(size: Int, init: (Int) -> T)将初始化器 function 作为第二个参数的构造函数来实现。

通过在源数组上调用map

init {
    val deepCopy = randomboard.map { it.copyOf() }.toTypedArray()
    _preFillButtons.postValue(deepCopy)
}

通过使用Array<T>(size: Int, init: (Int) -> T)构造函数

init {
    // Same as: val deepCopy = Array(randomboard.size) { idx -> randomboard[idx].copyOf() } }
    val deepCopy = randomboard.let { Array(it.size) { idx -> it[idx].copyOf() } }
    _preFillButtons.postValue(deepCopy)
}

然后,如果您通过引用比较所有值,您将看到现在内部 arrays 也是一个新实例。

init {
    val deepCopy = randomboard.map { it.copyOf() }.toTypedArray()
    Log.d("Test", "deepCopy === randomboard: ${deepCopy === randomboard}") // false
    Log.d("Test", "deepCopy[0] === randomboard[0]: ${deepCopy[0] === randomboard[0]}") // false
    Log.d("Test", "deepCopy[1] === randomboard[1]: ${deepCopy[1] === randomboard[1]}") // false
    Log.d("Test", "deepCopy[2] === randomboard[2]: ${deepCopy[2] === randomboard[2]}") // false
    Log.d("Test", "deepCopy[3] === randomboard[3]: ${deepCopy[3] === randomboard[3]}") // false
    // ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM