androidkotlinandroid-architecture-componentsandroid-architecture

Correct way to pass events back from a view in Android


I've been following the UI architecture laid out in this article https://developer.android.com/jetpack/guide/ui-layer which essentially amounts to this:

Android UI Architecture

It works great, but there is no example given in the article of how to pass events back from the UI elements to the ViewModel (in the case of an onclick event for example).

I have the following code in my MainActivity:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val loginViewModel: LoginViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val loginView = LoginView(layoutInflater)
        setContentView(loginView)
    }

    fun setContentView(iView: IView) {
        super.setContentView(iView.contentView)
    }
}

I could easily add something to LoginView such as LoginView.setOnSubmitClickedListener() and then trigger an event in my ViewModel from MainActivity. I'm just wondering if this is the correct way to do it or if there's something better?


Solution

  • It gets into it further down in the section on unidirectional data flow - or there's a separate article on events (you're looking at the overview page)

    This is the example they use:

    class LatestNewsActivity : AppCompatActivity() {
    
        private lateinit var binding: ActivityLatestNewsBinding
        private val viewModel: LatestNewsViewModel by viewModels()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            /* ... */
    
            // The expand section event is processed by the UI that
            // modifies a View's internal state.
            binding.expandButton.setOnClickListener {
                binding.expandedSection.visibility = View.VISIBLE
            }
    
            // The refresh event is processed by the ViewModel that is in charge
            // of the business logic.
            binding.refreshButton.setOnClickListener {
                viewModel.refreshNews()
            }
        }
    }
    

    They have two kinds of events there - one that's purely about the UI (whether part of it is expanded or not), and one that actually relates to the underlying data in some way.

    The ViewModel doesn't need to care about the UI state, so that's handled directly in the UI. But when it comes to refreshing the data, they call a handler function on the ViewModel itself. And because of the observer pattern, if that event causes some change in the data in the VM, the observer will see it and update the UI in response.

    So you don't actually update the UI directly, in response to the event! You update the VM, and the UI will update itself because of how everything is wired up.