maschin
maschin

Reputation: 125

How to keep user inputs on screen orientation change with Android DataBinding library?

I'm at the very beginning of a new Android project. After playing around with MVP in my last project, I want to implement MVVM with Data Binding this time.

I have a problem understanding DataBinding correctly when it comes to configuration changes like screen orientation change.

All DataBinding samples out there (all I have found when looking for "android mvvm databinding") have the same problem: When I enter something in an EditText and rotate the screen, the EditText is empty afterwards.

As soon as I have something like the following in my layout, I can't get the views (EditText in this case) to restore their state after screen rotation change.

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="vm"
            type="com.example.app.TestViewModel" />
    </data>

    <EditText android:id="@+id/question"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@={vm.question}" 
        android:hint="Question" />

</layout>

I guess this is because of assigning a new view model instance in the activities onCreate method every time.

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityTestBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_test);
    binding.setVm(new TestViewModel());
}

How do you handle that correctly?

I can't develop an app with several forms that forget all user inputs at screen orientation.

Upvotes: 5

Views: 5511

Answers (2)

Sergei Bubenshchikov
Sergei Bubenshchikov

Reputation: 5371

You need to save state of your view model by overriding of method onSaveInstanceState()in your activity class and restore it in onCreate() method.

private static final String QUESTION = "testViewModel.question";
private TestViewModel mTestViewModel;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityTestBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_test);

    mTestViewModel = new TestViewModel();
    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
         // restore view model state
         String questionVal = savedInstanceState.getString(QUESTION, "");
         mTestViewModel.setQuestion(questionVal);
    }
    binding.setVm(mTestViewModel);
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save current view model state
    savedInstanceState.putString(QUESTION, mTestViewModel.getQuestion());

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

More information about "save-restore" technology you can read at this part of documentation

Upvotes: 3

BladeCoder
BladeCoder

Reputation: 12949

Two-way data binding is actually compatible with automatic view state restoration, but for it to work you need to apply the binding before the view state is restored.

By default, data binding defers all bindings until the next layout pass, which occurs after the view state has been restored. This means the view state will actually be restored, but then the binding value(s) will overwrite the current values of the views.

To apply binding immediately, call executePendingBindings() before the view state is restored, for example in Activity.onCreate():

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityTestBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_test);

    viewModel = new TestViewModel();
    binding.setVm(viewModel);
    binding.executePendingBindings();
}

And that's all you need to do. Of course, the views need to have an android:id attribute in order for their state to be saved and restored automatically.

When the view state is restored, the restored values will also be assigned to the properties bound using two-way binding so your ViewModel will reflect the restored state.

Upvotes: 23

Related Questions