0

I have an activity that should display a non-fixed amount of fragments. Each fragment will receive an element of a MutableList of Customer and it needs to have its components bound to a property in this Customer class.

I've created the class like this:

class Customer constructor(
    val name: LiveData<String>
    val email: LiveData<String>
)

In my ViewModel I've created the MutableList of Customer:

class CustomerViewModel: ViewModel() {

    val customers: MutableList<Customer> = mutableListOf()
}

Then I dynamically create the Fragments:

for (i in 0..customerCount) {
  val fragment = FragmentCustomer.newInstance(viewModel.customers.get(i))
  supportFragmentManager.beginTransaction()
    .add(R.id.fragmentContainer, fragment, "FragmentCustomer_$i")
    .commit()
}

The customer instance is stored in a variable in the Fragment and then I do the DataBindingUtil.inflate:

var binding: FragmentCustomerBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_customer, container, false)
var view = binding.root

binding.model = palavra
return view

And in the Fragment's layout:

<EditText
    android:id="@+id/nameTextView"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:text="@{model.name}"
    android:onTextChanged="@={model.name}" />

This is causing ****/ data binding error ****msg:The expression modelName.getValue() cannot be inverted: 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.

How can I fix this? And a "bonus" question, do I still have to store view content using savedState on events like onDestroy or ViewModel will handle that for me?

1 Answer 1

1

For your first question, the values should be MutableLiveData types instead of just LiveData, otherwise there will be no setter exposed:

class Customer constructor(
    val name: MutableLiveData<String>
    val email: MutableLiveData<String>
)

Don't forget to set the lifecycle owner on the binding after inflating it, because otherwise the view will not be updated when the data changes.

For your bonus question: You should store the ViewModel as a variable in your fragment. This ensures that it will keep the data when the view is being destroyed, but the fragment is kept. Upon destroy, the live data instances will be cleaned up, because they know about the lifecycle you provided to the binding.

Optimally, the Customer class should not have modifiable properties, because this makes it editable to everyone, without the guarantee of saving the changes. But that's a different story.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your answer, but I couldn't understand the last part of it. What do you mean by not having the guarantee of saving the changes? The whole idea is to have a method in my ViewModel persist the content of the MutableList with current field values. Won't this work?
One more thing, changing to MutableLiveData changed the error to ****/ data binding error ****msg:Cannot find the getter for attribute 'android:onTextChanged' with value type java.lang.String on android.widget.EditText.
onTextChanged should be a callback type, so something like this: "@{() -> viewModel.doSomething()}". The ViewModel should handle the changes indeed, you should be fine. What I meant is that if you have a very big application, you might forget to save it somewhere, and that might lead to errors.
Thanks again. I removed the onTextChanged and changed the text binding to android:text="@={model.name}"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.