Data binding & MVVM. Show DatePickerDIalog by clicking on the button

Viewed 4440

Now I have this code:

class MyFragment : DaggerFragment() {
    ...
    private fun setTimePickerDialog() {
        binding.timeButton.setOnClickListener{
            val calendar = viewModel.calendar
            val curHourOfDay = calendar.get(Calendar.HOUR_OF_DAY)
            val curMinute = calendar.get(Calendar.MINUTE)
            val dialog = TimePickerDialog(context, { _, hourOfDay, minute ->
                 val c = Calendar.getInstance()
                 c.set(1970, 0, 1, hourOfDay, minute)
                 viewModel.time.value = SimpleDateFormat("HH:mm:ss").format(c.time)
            }, curHourOfDay, curMinute, true)
            dialog.show()
        }
    }
    ...
}

I want to take advantage of the DataBinding library and not write setOnClickListener in my fragment. But I can't move this code to the ViewModel, because a context is required. What is a clean way to show DatePickerDialog by clicking on the button using DataBinding and MVVM?

3 Answers

why can't you use a handler inside the xml instead of the viewmodel. Then get the click using the handler. In your XML -->use a variable called handler whose type is the path of your fragment

<data class ="binding">
    <variable name="handler" type="com.MyFragment"/>
</data>

android:onClick="@{()->handler.onDisplayTimePickerDialogClick()}

What you are looking for, can be achieved using LiveData from the architecture component, it can also be done with any other observable tools such as rxJava.

The first step is to define an observable in our ViewModel, and implementing a function that when the user clicks, it invokes the observable.

ViewModel:

MutableLiveData<Boolean> timePickerDialogData = new MutableLiveData<>();
...

public void onDisplayTimePickerDialogClick() {
    timePickerDialogData.setValue(true);
}

...
public LiveData<Boolean> getTimePickerDialogData() {
        return timePickerDialogData;
    }

The second step is to observe the observable in the fragment and listen to its value changes. when the observable is invoked from ViewModel we can display the dialog.

Fragment:

private void observeTimePickerDialogData() {
    viewModel.getTimePickerDialogData().observe(this, display -> {
    if(display) setTimePickerDialog(); // Display TimePickerDialog
    });
}

Finally, the onClick logic inside the XML layout file using android data binding.

XML:

android:onClick="@{()->viewModel.onDisplayTimePickerDialogClick()}"

I think it can be done more directly with a onClickMethod on the ViewModel. By passing the view as param into the onClick-Method we can use the view's context to start the TimePickerDialog:

My clickable TextView looks like this (only relevant attributes contained):

    <TextView
        android:onClick="@{(view) -> viewmodel.onChangeTimeClicked(view)}"/>

My ViewModel's onClicked-Method then looks like this:

    fun onChangeTimeClicked(v: View) {
        val timeSetListener = TimePickerDialog.OnTimeSetListener { _,_,_ ->
            // Some Implementation
        }
        TimePickerDialog(
            v.context,          // <-- Context from view on which was clicked
            timeSetListener,
            0,
            0,
            true
        ).show()
    }

I know the question is a bit older but as I had this issue now I found this thread and maybe it is also still useful for somebody.

Related