Search code examples
androidkotlinconstructorviewmodelandroid-viewmodel

Retrieve param to initiate Android vielmodel (init vs constructor)


I'm trying to instantiate my ViewModel so that depending on which param given it will show me a different list. However, I am a bit confused about the init and constructor calls.

The below version gives me a MainViewModel> has no zero argument constructor error.

Version 1

class MainViewModel(private val param: String) : ViewModel() {

private var list: ArrayList<ModelDialogOption>? = null

val userMutableLiveData: MutableLiveData<ArrayList<ModelDialogOption>?> = MutableLiveData()

init {
    populateList()
    userMutableLiveData.value = list!!
}

private fun populateList() {

    list = ArrayList()

    when (param) {

        "Choose your age range" -> {

            list!!.add(ModelDialogOption("Prefer not to say", false))
            list!!.add(ModelDialogOption("16-39", false))
            list!!.add(ModelDialogOption("40-59", true))
            list!!.add(ModelDialogOption("60+", false))

        }

    }

}

}

Version 2

Whereas this gives me an empty list not finding the param since the constructor is only called after the init already took place:

class MainViewModel() : ViewModel() {

var param: String? = null
private var list: ArrayList<ModelDialogOption>? = null

val userMutableLiveData: MutableLiveData<ArrayList<ModelDialogOption>?> = MutableLiveData()

init {
    populateList()
    userMutableLiveData.value = list!!
}

constructor(param: String) : this() {
    this.param = param
    populateList()
    userMutableLiveData.value = list!!
}

private fun populateList() {

    list = ArrayList()

    when (param) {

        "Choose your age range" -> {

            list!!.add(ModelDialogOption("Prefer not to say", false))
            list!!.add(ModelDialogOption("16-39", false))
            list!!.add(ModelDialogOption("40-59", true))
            list!!.add(ModelDialogOption("60+", false))

        }

    }

}

}

And here another also with the empty list:

Version 3

class MainViewModel() : ViewModel() {

var param: String? = null
private var list: ArrayList<ModelDialogOption>? = null

val userMutableLiveData: MutableLiveData<ArrayList<ModelDialogOption>?> = MutableLiveData()

constructor(param: String) : this() {
    this.param = param
    populateList()
    userMutableLiveData.value = list!!
}

private fun populateList() {

    list = ArrayList()

    when (param) {

        "Choose your age range" -> {

            list!!.add(ModelDialogOption("Prefer not to say", false))
            list!!.add(ModelDialogOption("16-39", false))
            list!!.add(ModelDialogOption("40-59", true))
            list!!.add(ModelDialogOption("60+", false))

        }

    }

}

}

Version 4

And this gives me the list populate but sure without a param since not passing any but just using it as my starting point:

class MainViewModel : ViewModel() {

private var list: ArrayList<ModelDialogOption>? = null

val userMutableLiveData: MutableLiveData<ArrayList<ModelDialogOption>?> = MutableLiveData()

init {
    populateList()
    userMutableLiveData.value = list!!
}

private fun populateList() {

    list = ArrayList()
            list!!.add(ModelDialogOption("Prefer not to say", false))
            list!!.add(ModelDialogOption("16-39", false))
            list!!.add(ModelDialogOption("40-59", true))
            list!!.add(ModelDialogOption("60+", false))

}

}

And this is my ViewModelFactory:

public class MyViewModelFactory implements ViewModelProvider.Factory {
    private String param;


    public MyViewModelFactory(String param) {
        this.param = param;
    }


    @NotNull
    @Override
    public <T extends ViewModel> T create(@NotNull Class<T> modelClass) {
        return (T) new MainViewModel(param);
    }
}

Initiated this way:

private val viewModel: MainViewModel by activityViewModels {
    MyViewModelFactoryForHashMap(
        arguments?.getString("headerText")
    )
}

Thank you very much.


Solution

  • Since I answered your last question I know little bit your code flow

    Instead of passing param in ViewModel you should have update it because you are sharing and listening with two fragments

    class CovidCheckInFragment : Fragment(R.layout.fragment_covid_check_in) {
    
    var navController: NavController? = null
    private val model: MainViewModel by  activityViewModels()
    private lateinit var tartTextView:TextView
    
    
    @RequiresApi(Build.VERSION_CODES.M)
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    
        tvHeader.text = "COVID Check-in"
    
        navController = Navigation.findNavController(view)
    
    
        // Listeners
        etYourAge.setOnClickListener(onClick)
        etYourCounty.setOnClickListener(onClick)
        etYourLocality.setOnClickListener(onClick)
    
        rbFemale.setOnClickListener(onClickRb)
        rbMale.setOnClickListener(onClickRb)
        rbPreferNotToSay.setOnClickListener(onClickRb)
        
        model.userMutableLiveData.observe(viewLifecycleOwner, Observer {
            it?.let {
                it.filter {
                    it.selected == true
                }.map {
                    tartTextView.text = it.title
                }
            }
        })
    
    }
    
    
    private val onClick = View.OnClickListener {
    
        val destination: Int = R.id.action_covidCheckInFragment_to_my_dialog_fragment
    
        val message: String? = when (it.id) {
            R.id.etYourAge -> {
                tartTextView = etYourAge
                model.updateList("Choose your age range")
                "Choose your age range"
            }
            R.id.etYourCounty -> {
                tartTextView = etYourCounty
                model.updateList("Choose your county")
                "Choose your county"
            }
            R.id.etYourLocality -> {
                tartTextView = etYourLocality
                model.updateList("Choose your locality")
                "Choose your locality"
            }
            else -> {
                ""
            }
        }
    
    
        val bundle = bundleOf("headerText" to message)
        navController = Navigation.findNavController(it)
    
        navController!!.navigate(
            destination,
            bundle
        )
    
    }
    
    
    @RequiresApi(Build.VERSION_CODES.M)
    private val onClickRb = View.OnClickListener {
    
    
        val list = listOf<Button>(rbFemale, rbMale, rbPreferNotToSay)
    
        for (i in list) {
            i.setTextColor(resources.getColor(R.color.text))
            i.background.setTint(ContextCompat.getColor(activity as MainActivity, R.color.greyEee))
        }
    
        if ((it as AppCompatRadioButton).isChecked) {
    
            it.setTextColor(resources.getColor(R.color.white))
            it.background.setTint(ContextCompat.getColor(activity as MainActivity, R.color.shadow))
    
        }
    }
    

    }

    Here is your MainViewModel

    class MainViewModel : ViewModel() {
    
    private var list  = ArrayList<ModelDialogOption>()
    
    val userMutableLiveData: MutableLiveData<ArrayList<ModelDialogOption>> = MutableLiveData()
    
    fun updateItem(position: Int) {
        val itemToUpdate = list[position]
        itemToUpdate.selected = !itemToUpdate.selected!!
        list[position] = itemToUpdate
    }
    
    fun flushItems() {
        userMutableLiveData.value = list
    }
    
    fun updateList(param: String) {
        list = ArrayList()
        when (param) {
    
            "Choose your age range" -> {
    
                list.add(ModelDialogOption("Prefer not to say", false))
                list.add(ModelDialogOption("16-39", false))
                list.add(ModelDialogOption("40-59", false))
                list.add(ModelDialogOption("60+", false))
    
            }
    
            "Choose your county" -> {
    
                list.add(ModelDialogOption("Prefer not to say", false))
                list.add(ModelDialogOption("County1", false))
                list.add(ModelDialogOption("County2", false))
                list.add(ModelDialogOption("County3", false))
    
            }
            "Choose your locality" -> {
                list.add(ModelDialogOption("Prefer not to say", false))
                list.add(ModelDialogOption("Locality1", false))
                list.add(ModelDialogOption("Locality2", false))
                list.add(ModelDialogOption("Locality3", false))
            }
        }
        flushItems()
    }
    

    }