简体   繁体   English

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

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

Hi i think i don't really now how live data works.嗨,我想我现在真的不知道实时数据是如何工作的。

I am having a 2D Array with prefilled Values.我有一个带有预填充值的二维数组。

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

And a Live Data Object that posts just once at init Function of my ViewModel the current State of my Array.还有一个实时数据 Object,它只在我的 ViewModel 的初始化 Function 发布一次我的阵列的当前 State。

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

So i want that only the current State of my Array and no future states are pushed to my LiveData Objects so i gave it a copy of my Array.所以我希望只有我的数组的当前 State 和没有未来的状态被推送到我的 LiveData 对象所以我给了它我的数组的副本。

When i am changing any value of my Array randomboard and then change like my orientation my livedata object has just this new Value in his state without that i pushed any new State to that LiveData object.当我改变我的阵列随机板的任何值然后像我的方向一样改变时,我的 livedata object 在他的 state 中只有这个新值,没有我将任何新的 State 推到那个 LiveData object。

Maybe Livedata object is not that what i need there but i don't know why even the copy object is updated.也许 Livedata object 不是我需要的,但我不知道为什么连副本 object 都更新了。

UPDATE:更新:

The complete Code完整代码

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()
}
}

ViewModel:视图模型:

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
}
}

Seeing the new state after a screen rotation (after a configuration change) is not related to how LiveData works.屏幕旋转后(配置更改后)看到新的 state 与 LiveData 的工作方式无关。 You are observing your LiveData correctly.您正在正确观察您的 LiveData。

So i want that only the current State of my Array and no future states are pushed to my LiveData Objects so i gave it a copy of my Array.所以我希望只有我的数组的当前 State 和没有未来的状态被推送到我的 LiveData 对象所以我给了它我的数组的副本。

A slight correction here, you gave it a shallow copy of your array.这里稍作更正,您给了它一个数组的浅表副本。 A shallow copy is a copy of the original array, but if the items are instances (references to objects), they will be copied by reference.浅拷贝是原始数组的副本,但如果项目是实例(对对象的引用),它们将通过引用进行复制。 Which means that changes on items in the copy of the array will affect the items in the original array as well, since it is the same references (= same instances).这意味着对数组副本中项目的更改也会影响原始数组中的项目,因为它是相同的引用(=相同的实例)。

And since you have an array of arrays, items in your array are instances of 4 other arrays. And when you change the values inside randomboard in your trySetValue function, you access the same 4 array instances every time.因为你有一个 arrays 的数组,你的数组中的项目是 4 个其他 arrays 的实例。当你在randomboard中更改trySetValue内的值时,你每次都会访问相同的 4 个数组实例。

The arrayOf calls create a new array instance and fill it with the values provided as parameters. arrayOf调用创建一个新的数组实例并用作为参数提供的值填充它。 So your initial assignment to randomboard creates 5 array instances:因此,您对randomboard的初始分配会创建 5 个数组实例:

  • 4 are of type Integer[] and contain values 0, 0, 0, 0 4 是 Integer[] 类型,包含值 0、0、0、0
  • 1 is of type Integer[][] and contains references to the 4 arrays above. 1 是 Integer[][] 类型,包含对上面 4 arrays 的引用。 The reference to this array is stored into the field randomboards对该数组的引用存储在字段randomboards

This snippet of code compares the values by reference and shows that the main array is a new instance (because of the copyOf call), but the arrays inside are the same instances.这段代码通过引用比较值,显示主数组是一个新实例(因为调用了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
    //...
}

If you don't want the changes to affect the copy of the array, you have to make a deep copy .如果你不想改变影响数组的副本,你必须做一个深拷贝 Which you can do by calling .map on the source array or using the Array<T>(size: Int, init: (Int) -> T) constructor that takes the initializer function as a 2nd parameter.您可以通过在源数组上调用.map或使用Array<T>(size: Int, init: (Int) -> T)将初始化器 function 作为第二个参数的构造函数来实现。

By calling map on the source array通过在源数组上调用map

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

By using the Array<T>(size: Int, init: (Int) -> T) constructor通过使用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)
}

Then if you compare all values by reference, you will see that now also the inner arrays are a new instance.然后,如果您通过引用比较所有值,您将看到现在内部 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.

相关问题 使用 LiveData 更新实现 FirestoreQuery:Android Firestore RecyclerView - Implement FirestoreQuery with LiveData updates : Android Firestore RecyclerView Android MVVM + Room 通过其他 LiveData 对象创建 LiveData RecyclerViewItem 对象 - Android MVVM + Room creating LiveData RecyclerViewItem objects by other LiveData objects 带有状态的 LiveData(不是 Flow) - LiveData (not Flow) with states Android LiveData - 观察复杂/嵌套的对象 - Android LiveData - observe complex/nested objects Android体系结构组件(LiveData):一旦更新了Textview,如何从LiveData中清除所有值 - Android Architecture Component(LiveData): How to clear all value from LiveData once it updates a textview 线程安全的 LiveData 更新 - Thread safe LiveData updates 如何使用 LiveData 处理错误状态? - How to handle error states with LiveData? 如何观察多个LiveData的状态? - How to observe the states of multiple LiveData? UI 观察 LiveData <list<myobject> > 显示新添加和新删除的对象,但不显示更新或更改</list<myobject> - UI Observing LiveData<List<MyObject>> Shows Newly-Added and Newly-Removed Objects, but Not Updates or Changes 在多个进程中获取 LiveData 更新 - Getting LiveData updates in multiple processes
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM