= _user fun onBtnClick(){ Log.i(" /> = _user fun onBtnClick(){ Log.i(" /> = _user fun onBtnClick(){ Log.i("/>

Android Two Way Databinding working only one way

3k views Asked by At

I have ViewModel:

class MyViewModel : ViewModel(){
    private val _user = MutableLiveData("")

    val user: LiveData<String> = _user

    fun onBtnClick(){
        Log.i("MyViewModel", "user: ${_user.value}")
    }
}

I connected it with fragment layout using:

<data>
    <variable
        name="viewmodel"
        type="com.pkg.pkg.pkg.fragments.concretePkg.MyViewModel" />
</data>
<EditText
    ...
    android:text="@={viewmodel.user}"
    android:hint="User" />

And in fragment class in onCreateView method:

binding = DataBindingUtil.inflate(inflater, R.layout.fragment_my_fragment, container, false)
binding.lifecycleOwner = this
// bind viewModel
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
binding.viewmodel = viewModel

// Inflate the layout for this fragment
return binding.root

I can change the EditText value from the fragment or ViewModel class, but when I try to get the value inside the onBtnClick method of the ViewModel in the Logcat I am getting the message: I/MyViewModel: user: What am I missing here so that two-way data binding works from the other direction too? I nutshell, data goes from ViewModel/ Fragment to EditText on the interface, but it doesn't flow from EditText on the interface to ViewModel fields/ methods. Thanks in advance.

------- EDITED ----- I managed to populate everything, but know I am getting this error:

Details: There is no inverse for method getValue, you must add an @InverseMethod annotation to the method to indicate which method should be used when using it in two-way binding expressions

I suppose that is because viewmodel.user is of type LiveData and not of a String. I have written binding adapter:

@BindingAdapter("android:text")
    fun setLiveDataText(editText: EditText, liveData: LiveData<String>) {
            if(liveData == null){
                editText.setText("")
            } else {
                editText.setText(liveData.value)
            }
        }

But I am still getting the above message.

2

There are 2 answers

4
Alvin Dizon On

First, you need to make _user public. Then you need to use "@={viewmodel._user}", so that you can get the value of _user. Databinding API can't figure out and generate code if you don't make your databinding variable public. For simple fields such as Strings, this approach will suffice, but you may have to implement BindingAdapters for more complex situations. See this article for examples.

0
M.Vas On

The answer to this question in order to work you should add this to build.gradle:

implementation "android.arch.lifecycle:extensions:1.1.1"

And kotlin version should be downgraded:

ext.kotlin_version = '1.3.41'

Cheers!