簡體   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