关于android:双向数据绑定(在xml中),ObservableField,BaseObservable,我应该使用哪种双向数据绑定?

Two-way databinding(in xml), ObservableField, BaseObservable , which one I should use for the two-way databinding?

我已经使用了一段时间的数据绑定,即使现在它现在也不适用于JDK 8和API 24。我仍然找到一种以更简单的方式使用数据绑定的方法。但是,当我使用以下方式进行精确的双向数据绑定时(在我看来,双向数据绑定就是这里的东西(什么是双向方式?),所以发生了一些奇怪的事情。

1.双向数据绑定(在xml中)

1
android:text="@={testStr}"

官方文档中没有提到这一点(https://developer.android.com/topic/libraries/data-binding/index.html,此页面通常已更新,可能现在已更改)。但是可以将变量绑定到xml。

2.属性的ObservableField

此处的示例(https://developer.android.com/topic/libraries/data-binding/index.html#observablefields)

1
2
3
4
5
6
7
private static class User {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}

3.将模型类扩展到BaseObservable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getLastName() {
       return this.lastName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
       notifyPropertyChanged(BR.lastName);
   }
}

必须将模型类扩展为BaseObservable类,并且getter方法必须使用" @Bindable"进行注释,并且setter方法需要在绑定xml中使用相应的命名调用方法notifyPropertyChange()。

我的问题是,我想知道三种绑定方法的缺点和优点。当然,我知道第一个会更容易。但是有一段时间我在文档和某些网站中找到了。它在下一刻消失了。官方文档已更改,没有任何明确的声明。我仍然想知道我应该使用第一种方法,所以我必须准备更改方法2或3。

Student_XML2WAY.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Student_XML2WAY {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int pAge) {
        age = pAge;
    }
    public String getName() {
        return name;
    }
    public void setName(String pName) {
        name = pName;
    }
}

Student_ObserField.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Student_ObserField {
    private ObservableInt age;
    private ObservableField<String> name;
    public Student_ObserField() {
        age = new ObservableInt();
        name = new ObservableField<>();
    }
    public ObservableInt getAge() {
        return age;
    }
    public ObservableField<String> getName() {
        return name;
    }
}

Student_Extend.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Student_Extend  extends BaseObservable{
    private int age;
    private String name;

    @Bindable
    public int getAge() {
        return age;
    }
    public void setAge(int pAge) {
        age = pAge;
        notifyPropertyChanged(BR.student3);
    }
    @Bindable
    public String getName() {
        return name;
    }
    public void setName(String pName) {
        name = pName;
        notifyPropertyChanged(BR.student3);
    }
}

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="student1"
            type="example.com.testerapplication.sp.bean.Student_XML2WAY"/>

        <variable
            name="student2"
            type="example.com.testerapplication.sp.bean.Student_ObserField"/>

        <variable
            name="student3"
            type="example.com.testerapplication.sp.bean.Student_Extend"/>

    </data>

    <LinearLayout

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
      >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@={student1.name}"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{student2.name}"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{student3.name}"/>
        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="update"/>
    </LinearLayout>
</layout>

活动课

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MainActivity extends AppCompatActivity {
    private Student_XML2WAY mStudent1;
    private Student_ObserField mStudent2;
    private Student_Extend mStudent3;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.activity_main, null, false);
        mStudent1 = new Student_XML2WAY();
        mStudent1.setName("XML First");
        mStudent2 = new Student_ObserField();
        mStudent2.getName().set("ObserField Second");
        mStudent3 = new Student_Extend();
        mStudent3.setName("Extend Third");
        binding.setStudent1(mStudent1);
        binding.setStudent2(mStudent2);
        binding.setStudent3(mStudent3);
        setContentView(binding.getRoot());
        binding.btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mStudent1.setName("Student1");
                mStudent2.getName().set("Student2");
                mStudent3.setName("Student3");
            }
        });
    }
}


您的Student_XML2WAY.java不适用于2向绑定,因为它不能满足2way绑定的要求(BaseObservableBindable或类似的东西)。

如果我将直接访问模型,则将使用BaseObservable,就像在您的Student_Extend中一样。我在Activity中将有一个Student_Extend实例,并在onCreate中设置变量:

1
2
3
4
Student mStudent = new Student("John Doe", 42); //
binding.setStudent(mStudent);
//later:
mStudent.setAge(37);

如果正确实施,这还将更改UI(以及模型)中的Age

如果您不想直接访问模型并想使用ViewModel,请使用ObervableFields

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Student {
    private String name;
    private int age;
    //Corresponding setters and getters
}


public class StudentViewModel {
    private ObservableField<Student> mStudentField = new ObservableField<>();

    //if I have a large model class, and only want to use some fields,
    //I create some getters (and setters, for the two way attributes)
    //Something like this:

    public int getAge() {
        return mStudentField.get().getAge();
    }
    public void setAge(int newAge) {
        return mStudentField.get().setAge(newAge);
    }
}

因此,我在Activity中创建了StudentViewModel的实例并将其设置为绑定。伪xml如下所示:

1
2
3
4
5
6
7
8
<layout>
    <data>
        <variable name="studentViewModel"
                  type="locaction.of.StudentViewModel"> <!-- or do an import -->
    </data>
    <EditText
        android:text="@={studentViewModel.age}"/>
</layout>

因此,ViewModel方法更加"清晰",因为您几乎将与视图有关的所有事情都外包了。将您的BindingAdapter,单击方法,转换器方法放在此处,并保持Activity干净。此外,您不会直接更改模型。
对于简单的班级和项目,这种方法可能会显得过大。 ;)

如果要查看使用DataBinding和MVVM的完整示例,请查看有关此方法的Droids on roids方法。


我觉得ObservableField方法是必经之路,因为不需要编写getter / setters或调用notifyPropertyChanged

另外,如果您有一个自定义对象ObservableField studentField,并且使用了android:text="@{viewModel.studentField.name},则调用studentField.set(newStudent)时,文本的确会更新。

我发现RxJava非常有用。 ObservableField可以轻松转换为rx.Observable,反之亦然。这允许使用Rx运算符。如果您有兴趣,可以在此处检查实现:FieldUtils.java