Home > Software design >  Can't change the value of MutableList in a Kotlin class
Can't change the value of MutableList in a Kotlin class

Time:04-02

I want to add the random generated integer into my MutableList in Player class when I use the random integer generator method located in Player class in my fragment then I want to pass this MutableList to Fragment with using Livedata(I'm not sure if i'm doing right with using livedata).Then show the MutableList in TextView.But MutableList returns the default value not after adding. So what am i doing wrong ? What should i do ? Thank you

MY CLASS

open class Player {

    //property
    private var playerName : String
    private var playerHealth : Int
    var playerIsDead : Boolean = false

    //constructor
    constructor(playerName:String,playerHealth:Int){
        this.playerName = playerName
        this.playerHealth = playerHealth
    }


    var numberss: MutableList<Int> = mutableListOf()

    fun attack(){
        //Create a random number between 1 and 10
        var damage = (1..10).random()

        //Subtract health points from the opponent

        Log.d("TAG-DAMAGE-WRITE","$damage")

        numberss.add(damage)
        Log.d("TAG-DAMAGE-ADD-TO-LIST","$numberss")

        Log.d("TAG-NUMBER-LIST-LATEST-VERSION","$numberss")

    }

}

MY FRAGMENT

class ScreenFragment : Fragment() {

    var nickname : String? = null
    private lateinit var viewModel : DenemeViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_screen, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel = ViewModelProvider(this).get(DenemeViewModel::class.java)


        arguments?.let {
            nickname = ScreenFragmentArgs.fromBundle(it).nickName
        }
        sFtextView.text = "Player : $nickname"
        action()

    }

    private fun action(){
        val health1 = (1..50).random()
        val health2 = (1..50).random()

        val superMan = Superman("Dusman",health1,)

        while(superMan.playerIsDead == false){
            //attack each other
            superMan.attack()
            sFtextsonuc.text = "Superman oldu"
            viewModel.setData()
            observeLiveData()

           superMan.playerIsDead = true
        }
    }

    fun observeLiveData(){
        viewModel.damageList.observe(viewLifecycleOwner, Observer { dmgList ->

            dmgList?.let {

                sFtextsonuc.text = it.toString()
                Log.d("TAG-THE-LIST-WE'VE-SENT-TO-FRAGMENT","$it")
            }

        })
    }
}

MY VIEWMODEL

class DenemeViewModel : ViewModel() {

    val damageList:MutableLiveData<MutableList<Int>> = MutableLiveData()

    fun setData(){
        damageList.value = Superman("",2).numberss
        Log.d("TAG-VIEWMODEL","${damageList.value}")
    }


}

MY LOG

PHOTO OF THE LOGS

CodePudding user response:

Your Superman is evidently part of some ongoing game, not something that should be created and then destroyed inside the action function. So you need to store it in a property. This kind of state is usually stored in a ViewModel on Android so it can outlive the Fragment.

Currently, you are creating a Superman in your action function, but anything created in a function is automatically sent do the garbage collector (destroyed) if you don't store the reference in a property outside the function.

Every time you call the Superman constructor, such as in your line damageList.value = Superman("",2).numberss, you are creating a new instance of a Superman that has no relation to the one you were working with in the action() function.

Also, I recommend that you do not use LiveData until you fully grasp the basics of OOP: what object references are, how to pass them around and store them, and when they get sent to the garbage collector.

So, I would change your ViewModel to this. Notice we create a property and initialize it with a new Superman. Now this Superman instance will exist for as long as the ViewModel does, instead of only inside some function.

class DenemeViewModel : ViewModel() {

    val superman = Superman("Dusman", (1..50).random())

}

Then in your frgament, you can get this same Superman instance anywhere you need to use it, whether it be to deal some damage to it or get the current value of its numberss to put in an EditText.

Also, I noticed in your action function that you have a while loop that repeatedly deals damage until Superman is dead. (It also incorrectly observes live data over and over in the loop, but we'll ignore that.) The problem with this is that the while loop is processed to completion immediately, so you won't ever see any of the intermediate text. You will only immediately see the final text. You probably want to put some delays inside the loop to sort of animate the series of events that are happening. You can only delay easily inside a coroutine, so you'll need to wrap the while loop in a coroutine launch block. In a fragment when working with views, you should do this with viewLifecycleOwner.lifecycleScope.launch.

Finally, if you set playerIsDead to true on the first iteration of the loop, you might as well remove the whole loop. I'm guessing you want to wrap an if-condition around that line of code. But since your code above doesn't modify player health yet, there's no sensible way to determine when a player should be dead, so I've commented out the while loop

private fun action() = viewLifecycleOwner.lifecycleScope.launch {
    val superMan = viewModel.superman

    //while(superMan.playerIsDead == false){
        //attack each other
        superMan.attack()
        sFtextsonuc.text = "Superman oldu"
        delay(300) // give user a chance to read the text before changing it
        sFtextsonuc.text = superMan.numberss.joinToString()
        delay(300) // give user a chance to read the text before changing it

        // TODO superMan.playerIsDead = true
    //}
}
  • Related