我们如何获取数据绑定以使用保存的实例状态? [英] How Do We Get Data Binding To Use Saved Instance State?

查看:41
本文介绍了我们如何获取数据绑定以使用保存的实例状态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL; DR:如果用于数据绑定的布局具有EditText,并且有android:text的绑定表达式,则该绑定表达式将覆盖已保存的实例状态值...即使我们未显式触发具有约束力的评估.清除配置更改之前用户输入的内容.我们如何解决此问题,以便在更改配置时使用保存的实例状态值?

TL;DR: If a layout used with data binding has an EditText, and there is a binding expression for android:text, the binding expression overwrites the saved instance state value... even if we do not explicitly trigger a binding evaluation. What the user typed in before the configuration change gets wiped out. How do we work around this, so that on a configuration change, the saved instance state value is used?

我们有一个愚蠢的Model:

public class Model {
  public String getTitle() {
    return("Title");
  }
}

我们有一个引用该Model的布局:

And we have a layout that references that Model:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <data>

    <variable
      name="model"
      type="com.commonsware.databindingstate.Model" />
  </data>

  <android.support.constraint.ConstraintLayout xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.commonsware.databindingstate.MainActivity">

    <EditText android:id="@+id/title"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:inputType="text"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent" />

  </android.support.constraint.ConstraintLayout>
</layout>

请注意,此布局没有绑定表达式.我们待会儿会讲到这一点.

Note that this layout has no binding expressions; we'll get to that in a bit.

在动态片段中使用布局:

The layout is used in a dynamic fragment:

public class FormFragment extends Fragment {
  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater,
                           @Nullable ViewGroup container,
                           @Nullable Bundle savedInstanceState) {
    return(MainBinding.inflate(inflater, container, false).getRoot());
  }
}

请注意,我们不会在任何地方调用setModel()来将Model推送到绑定中. MainBinding(用于上面显示的main.xml布局)仅用于填充布局.

Note that we are not calling setModel() anywhere to push a Model into the binding. The MainBinding (for the main.xml layout shown above) is just used to inflate the layout.

此代码(使用合适的FragmentActivity来设置FormFragment)正确使用了已保存的实例状态.如果用户在EditText中键入内容,然后旋转屏幕,则新创建的EditText会显示输入的文本.

This code (with a suitable FragmentActivity to set up the FormFragment) correctly uses the saved instance state. If the user types something into the EditText, then rotates the screen, the newly-recreated EditText shows the entered-in text.

现在,让我们更改布局以为android:text添加绑定表达式:

Now, let's change the layout to add a binding expression for android:text:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <data>

    <variable
      name="model"
      type="com.commonsware.databindingstate.Model" />
  </data>

  <android.support.constraint.ConstraintLayout xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.commonsware.databindingstate.MainActivity">

    <EditText android:id="@+id/title"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:inputType="text"
      android:text="@{model.title}"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent" />

  </android.support.constraint.ConstraintLayout>
</layout>

现在,如果用户在EditText中键入内容并旋转屏幕,则新创建的EditText为空.绑定表达式将覆盖从保存的实例状态恢复的任何框架.

Now, if the user types something into the EditText and rotates the screen, the newly-recreated EditText is empty. The binding expression overwrites whatever the framework restored from the saved instance state.

尽管我没有在绑定上调用setModel(),但这仍然存在.我当然可以看到如果在绑定上调用setModel()的位置,那么该位置将用模型中的数据替换EditText内容.但是我没有这样做.

This comes despite the fact that I am not calling setModel() on the binding. I can certainly see where if I called setModel() on the binding where that would replace the EditText contents with the data from the model. But I am not doing that.

我可以在官方设备(Google Pixel,Android 8.0)和生态系统设备(三星Galaxy S8,Android 7.1)上重现此行为.

I can reproduce this behavior on both official devices (Google Pixel, Android 8.0) and ecosystem devices (Samsung Galaxy S8, Android 7.1).

这可以通过自己保存状态并在某个时候恢复状态来手动"解决.例如,多条评论建议采用双向绑定,但这与其他设计目标(例如,不可变的模型对象)背道而驰.这似乎是数据绑定的一个相当基本的限制,所以我希望我遗漏了一些可以配置为自动使用保存的实例状态的东西.

This can be worked around "manually" by saving the state ourselves and restoring it at some point. For example, multiple comments have suggested two-way binding, but that runs counter to other design objectives (e.g., immutable model objects). This seems like a rather fundamental limitation of data binding, so I am hoping that there's something that I missed that I can configure to have the saved instance state be used automatically.

推荐答案

我认为ianhanniballake引用了相关答案,但也许还有更多答案.这是我对如何将该引用应用于这些情况的解释.

I thought that ianhanniballake had a reference to a relevant answer, but maybe there is more to it. Here is my interpretation of how that reference can be applied to these circumstances.

使用您提供的XML,以下代码将交替从保存的实例状态还原和从模型还原.还原保存的实例状态后,大概没有实例化要还原的模型.那就是mCount是偶数的时候.如果存在模型,则将基本上忽略已保存的实例状态,并且绑定将接管.这里的逻辑比我们想要的多,但是比明确保存和还原要少.

Using the XML that you presented, the following code will alternately restore from the saved instance state and restore from the model. When the saved instance state is restored then, presumably, there is not model instantiated to restore from. That is when mCount is even. If a model exists, then the saved instance state is basically ignored and the binding takes over. There is a little more logic here than we want, but it is less than saving and restoring explicitly.

mCount只是一种技巧.该模型是否存在的标志或其他指示将被使用.

mCount is just an artifice for the sake of the argument. A flag or other indication of whether the model exists or not would be used.

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private int mCount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mCount = (savedInstanceState == null) ? 0 : savedInstanceState.getInt("mCount", 0);
        if (mCount % 2 == 1) {
            // 1st, 3rd, 5th, etc. rotations. Explicitly execute the bindings and let the framework
            // restore from the saved instance state.
            binding.executePendingBindings();
        } else {
            // First creation and 2nd, 4th, etc. rotations. Set up our model and let the
            // framework restore from the saved instance state then overwrite with the bindings.
            // (Or maybe it just ignores the saved instance state and restores the bindnings.)
            Model model = new Model();
            binding.setModel(model);
        }
        mCount++;
    }

    @Override
    public void onSaveInstanceState(Bundle bundle) {
        super.onSaveInstanceState(bundle);
        bundle.putInt("mCount", mCount);
    }
}

这篇关于我们如何获取数据绑定以使用保存的实例状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆