Hoinzey

Javascript. Kotlin. Android. Java

Android: ViewModel

Viewmodels were introduced, in part, to help us deal with configuration changes. A configuration change causes your activity to be torn down and started again. Applications can take advantage of this by introducing different layouts and functionality on tablets when they are put into landscape


Configuration changes can also be a pain in the arse however. Viewmodels are helpful as they let us store some of our UI-related data outside of the activities lifecycle. Below are links to some posts which go into more detail


An Example

You can see viewmodels working with very little code. A text field holding a number and a screen rotation will do the job just fine. When you rotate the screen without using a viewmodel you'll lose the value. Below, the value is stored in the viewmodel which survives the configuration change, problem solved. Code found here.


Activity
Viewmodel
Portrait
Landscape
                            
    class ViewModelLiveDataActivity : AppCompatActivity() {

        lateinit var numberGenerator: ViewModelWithLiveData
    
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
    
            numberGenerator = ViewModelProvider(this).get(ViewModelWithLiveData::class.java)
    
            numberGenerator.getNumber().observe(this, Observer { number ->
                livedatatv.text = number
            })
    
            livedatabtn.setOnClickListener {
                numberGenerator.generateRandomNumber()
            }
        }
    }
                        
                            
    class ViewModelWithLiveData : ViewModel() {

        private var number: MutableLiveData = MutableLiveData("")
    
        fun getNumber(): MutableLiveData {
            return number
        }
    
        fun generateRandomNumber() {
            val random = Random
            number.value = "" + random.nextInt(0, 10)
        }
    }
                        

Multiple screens, one viewmodel

A viewmodel can be instantiated from an activity and referenced from several fragments which may represent multiple screens in your application. It uses databinding to update the UI dynamically and also handle click events


Doing this part of the post actually gave a good reminder of why I started this site. I use databiniding every day in work but most of those pages are long-standing and setup. When I went to use databining here however I quickly realised that I'd forgotten how to properly use databinding when it wasn't set up properly


You can find the code here

Activity
Fragment 1
Fragment layout
Screen image
                            
    class MultipleFragmentViewmodelActivity : AppCompatActivity() {

        lateinit var numberGenerator: ViewModelWithLiveData
    
        override fun onCreate(savedInstanceState: Bundle?) {
            ..
            numberGenerator = ViewModelProvider(this).get(ViewModelWithLiveData.TAG, ViewModelWithLiveData::class.java)
    
            val fragmentOne = MultipleViewmodelFragOne()
            val fragmentTwo = MultipleViewmodelFragTwo()
    
            val fragmentManager = supportFragmentManager
            val transaction = fragmentManager.beginTransaction()
    
            transaction
                    .add(R.id.fragmentOne, fragmentOne, "fragmentOne")
                    .add(R.id.fragmentTwo, fragmentTwo, "fragmentTwo")
                    .commit()
        }
    }
                        
                            
    class MultipleViewmodelFragOne : Fragment() {

        lateinit var numberGenerator: ViewModelWithLiveData
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            numberGenerator = ViewModelProvider(requireActivity()).get(ViewModelWithLiveData.TAG, ViewModelWithLiveData::class.java)
        }
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                                    savedInstanceState: Bundle?): View {
            val binding = inflate<FragmentViewmodelFragOneBinding>(inflater, R.layout.fragment_viewmodel_frag_one, container, false)
            binding.viewModel = numberGenerator
            binding.lifecycleOwner = requireActivity()
            return binding.root
        }
    }
                        
                            
    <layout
    ... ;>

    <data>
        <variable
            name="viewModel"
            type="com.example.learning.viewmodel.ViewModelWithLiveData" />
    </data>

        <FrameLayout
        ...
        tools:context=".viewmodel.multiple.MultipleViewmodelFragOne">

        <androidx.constraintlayout.widget.ConstraintLayout
            ... >

                <TextView
                    android:text="@={viewModel.number}"
                    ... />

                <Button
                    android:onClick="@{() -> viewModel.generateRandomNumber()}"
                    ... />

            </androidx.constraintlayout.widget.ConstraintLayout>

        </FrameLayout>

    </layout>
                        

Resources

First time I was learning about viewmodels I found some of these resources useful, hopefully you can too