Reputation: 419
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>
Upvotes: 9
Views: 1093
Reputation: 1593
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!
Upvotes: 1