[英]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 个数组实例:
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.