juliano.net
juliano.net

Reputation: 8177

Data binding to objects of a complex type in an MutableList

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?

Upvotes: 0

Views: 1333

Answers (1)

Daniel Zolnai
Daniel Zolnai

Reputation: 16920

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.

Upvotes: 1

Related Questions