Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically persist changes from two way data binding using Room?

Question

Two-Way DataBinding allows you to automatically populate UI components with data from an Object and then automatically update the Object as the user edits those UI components.

As the user edits UI components, is there a way to not only automatically update the Object in memory, but automatically update/persist the Object in a Room database?

I could manually listen to every UI field modification and manually save the Object to the Room Database. However, such a manual, brute force approach would negate the benefits of Two-Way DataBindings that I'm hoping to utilize.

Context

My application stores Items in an SQLite database using Android's Room Persistence Library. This is a simplified version of my Item:

@Entity
public class Item {

    @PrimaryKey(autoGenerate = true)
    private long id;

    private String name;

    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The magic of ViewModel, LiveData, and Two-Way DataBindings allows my ItemEditorFragment to automatically populate the UI with data from the selected Item and to update that Item when the user edits the UI components:

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof ItemViewModelProvider) {
        final ItemViewModelProvider itemViewModelProvider = (ItemViewModelProvider) context;
        mViewModel = itemViewModelProvider.getItemViewModel();
    } else {
        throw new RuntimeException(context.toString()
                + " must implement ItemViewModelProvider");
    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    final FragmentItemEditorBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_item_editor, container, false);
    binding.setViewModel(mViewModel);
    binding.setLifecycleOwner(this);

    final View view = binding.getRoot();
    return view;
}

This is a simplified version of the layout being inflated:

<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="viewModel"
            type="com.lafave.ItemViewModel" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={viewModel.selectedItem.name}"
            android:layout_gravity="center_horizontal"/>

    </LinearLayout>
</layout>
like image 648
Ed LaFave Avatar asked Jun 23 '18 00:06

Ed LaFave


People also ask

What is room persistence library?

The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite. Latest Update.

What is the purpose of two-way data binding?

Two-way data binding refers to sharing data between a component class and its template. If you change data in one place, it will automatically reflate at the other end. For example, if you change the value of the input box, then it will also update the value of the attached property in a component class.

How does room database work?

Room autogenerates implementations of your @Database and @Dao annotated classes the first time you compile your code after creating a Room Database. The implementation of UserDatabase and UserDao in the preceding example is generated automatically by the Room annotation processor.

What is 2 way data binding Android?

Two-way Data Binding is a technique of binding your objects to your XML layouts so that the layout can send data to your binding object. This is compared to a “traditional” or “one-way” Data Binding setup, where data would only move from your binding object to the layout.


1 Answers

One of the dirty hacks I've made work is: creating a new property, in the ViewModel (but it doesn't strictly have to be exactly there), without a backing field (in Java, this would probably equate to creating a getter and a setter, without a field):

    var selectedItemName: String?

        get() = selectedItem.value?.name

        set(value) {
            selectedItem.value?.let {
                    it.name = value
                    dao.update(it)
                }
        }

This is not an ideal solution, since it requires mirroring all of the properties in the class, but it works if you just want to get it over with. I'm awaiting a better solution!

like image 77
Aleksandar Stefanović Avatar answered Oct 22 '22 09:10

Aleksandar Stefanović