咱们什么都不说 先附上知识点
- 数据绑定的几种方式
- 点击事件绑定的几种方式
- 双向绑定数据
- RecycleView的数据绑定
- @InverseMethod 标签的使用
在DataBinding基本使用(2)中已经讲解了数据绑定的几种方式和点击事件绑定的几种方式,可能本人在某些地方理解偏差或错误,往广大读者指出并一起讨论、学习。
双向绑定
请过了摸索和摸坑,总结的来说双向绑定分两种
1.系统自带属性的双向绑定
2.自定义属性的双向绑定
系统自带属性的双向绑定
首先要注意的是,双向绑定用的符号@={} 而不在是@{}
既然是系统自带属性的双向绑定,那么我就就不需要在属性的问题上纠结太久,需要修改的是数据源Bean
public class StudentBean extends BaseObservable {
private String name = "";
private int old = 0;
private boolean studentis = false;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(com.example.scs.myapplication.BR.name);
}
@Bindable
public int getOld() {
return old;
}
public void setOld(int old) {
this.old = old;
notifyPropertyChanged(com.example.scs.myapplication.BR.old);
}
@Bindable
public boolean isStudentis() {
return studentis;
}
public void setStudentis(boolean studentis) {
this.studentis = studentis;
notifyPropertyChanged(com.example.scs.myapplication.BR.studentis);
}
}
我们可以看到 在set方法上需要加上@Bindable的注解,在get方法里需要调用notifyPropertyChanged(com.example.scs.myapplication.BR.studentis);可以理解为把参数注册到DataBinding中
public class BR {
public static final int _all = 0;
public static final int changedata = 1;
public static final int data = 2;
public static final int main = 3;
public static final int name = 4;
public static final int old = 5;
public static final int studentis = 6;
}
有的人会说,如果我有还多参数怎么吧?一个个写,一个个改不浪费时间吗?为此
DataBinding提供了响应式对象:
针对8种基本类型的数据结构提供了专门的包装类
- ObservableBoolean
- ObservableByte
- ObservableChar
- ObservableDouble
- ObservableFloat
- ObservableInt
- ObservableLong
- ObservableShort
这样就代替了 繁琐的set/get
public final ObservableField<String> name = new ObservableField<>();
public final ObservableInt old = new ObservableInt();
public final ObservableBoolean studemtis = new ObservableBoolean();
既然我们已经对数据惊喜了双向的处理,接下来就直接应用数据即可
既然要证明是双向的,我们多加了一个EditText去修改TextView的内容,看看对应的data是否改变
<variable
name="changedata"
type="com.example.scs.myapplication.StudentBean"></variable>
<variable
name="main"
type="com.example.scs.myapplication.Main2Activity"></variable>
<TextView
android:id="@+id/tv_change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@={changedata.name}"
android:textSize="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/et_change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:hint="双向绑定--改变view数据" />
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:onClick="@{main.main2click}"
android:text="修改" />
</LinearLayout>
这边是java代码
bean = new StudentBean();
bean.setName("双向绑定--Bean(非自定义)");
binding.setChangedata(bean);
binding.setMain(this);
public void main2click(View view) {
switch (view.getId()) {
case R.id.btn_1://双向绑定数据 需要 @={}
if (!TextUtils.isEmpty(binding.etChange.getText().toString())) {
binding.tvChange.setText(binding.etChange.getText().toString());
Toast.makeText(view.getContext(), bean.getName(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(view.getContext(), "不能为空", Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_2:
Intent intent = new Intent(Main2Activity.this, Main3Activity.class);
startActivity(intent);
break;
}
}
大家伙儿,都可以去试一试,你们会发现对应的bean的数据也发送了改变。
bean------->(赋值)TextView
TextView(修改)------->对应的bean的对应的数据修改
自定义属性的双向绑定
一看标题就应该知道,自定义属性,我们需要去处理自定义的数据,和双向的数据。双向的数据的处理方法同上,这里就不在做过多的解释。咱们重点讲解自定义数据如何去实现。
这里我们需要去理解一下几个属性的含义和使用方法
- InverseBindingMethods
- InverseBindingMethod
- InverseBindingAdapter
- BindingAdapter
BindingAdapter
我们先来讲解一下BindingAdapter,它其实就是用来实现自定义属性所对应的方法
<variable
name="tv1data"
type="String"></variable>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:mytext="@{tv1data}"
android:textSize="30dp" />
@Override
protected void onCreate(Bundle savedInstanceState) {
binding.setTv1data("tv1_data");//加载数据 way1}
@BindingAdapter("android:mytext")
public static void setMytext(TextView textView, String msg) {
textView.setText(msg + "");
}
上述代码就实现了,自定义属性的功能。
-
BindingAdapter 标签可以传两个参数
1.完整的写法 @BindingAdapter(value = {"android:onItemClick", "android:onLoadMore","android:loadMoreEnable"}, requireAll = false) 其中value当然就是自定义的属性的名字,requireAll表示所有属性是否都要用,true表示都要使用,false表示不用
2.自定义属性有几种表达形式
a.android:xxxx @BindingAdapter(“android:xxxx”)
b.app:xxxx @BindingAdapter(“app:xxxx”)
c.app:xxxx @BindingAdapter(“xxxx”)
使用b方法会报错,但是不影响编译 -
我们首先要定义了一个android:mytext的属性,因为是自定义的属性,所以我们需要提供一个对应的set方法,并且该方法能够起到响应的效果的方法。
-
setMytext方法必须传一个对应控件的对象进去。其次我们在自定义的属性里加了个参数,故在方法中也需要添加对应类别的对象进去。
-
方法必须要用static修饰
以上就是自定义属性的使用,举个例子我们可以对Imageview定义个自定义的属性,传入一个url地址,在对应的方法去给Imageview去设置图片
InverseBindingAdapter
- InverseBindingAdapter需要传两个参数 attribute:String类型 event: String类型
1.attribute 必填,表示当值发生变化时,要从哪个属性中检索这个变化的值,示例:"android:text"
2.event 非必填;如果填写,则使用填写的内容作为event的值;如果不填,在编译时会根据attribute的属性名再加上后缀“AttrChanged”生成一个新的属性作为event的值,举个例子:attribute属性的值为”android:text”,那么默认会在”android:text”后面追加”AttrChanged”字符串,生成”android:textAttrChanged”字符串作为event的值.
3.event属性的作用: 当View的值发生改变时用来通知dataBinding值已经发生改变了。开发者一般需要使用@BindingAdapter创建对应属性来响应这种改变。
先上XML代码,因为要实现双向,所以加了输入框去修改数据,来提现双向
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="main"
type="com.example.scs.myapplication.Main3Activity"></variable>
<variable
name="data"
type="com.example.scs.myapplication.StudentBean"></variable>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.example.scs.myapplication.MyEditText
android:id="@+id/my_et"
android:name="@={data.name}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{main.onMainClick}"
android:text="修改edittext" />
<Button
android:id="@+id/btn_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{main.onMainClick}"
android:text="修改bean" />
</LinearLayout>
</layout>
java代码
public class Main3Activity extends AppCompatActivity {
ActivityMain3Binding binding;
StudentBean bean;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main3);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main3);
binding.setMain(this);
bean = new StudentBean();
bean.setName("name");
binding.setData(bean);
}
public void onMainClick(View view) {
switch (view.getId()) {
case R.id.btn_1:
if (!TextUtils.isEmpty(binding.myEt.getText().toString())) {
binding.myEt.setName(binding.myEt.getText().toString());
Toast.makeText(view.getContext(), bean.getName() + "", Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_2:
bean.setName("修改Bean");
break;
}
}
}
自定义MyEditText
public class MyEditText extends android.support.v7.widget.AppCompatEditText {
private static String name = "";
@InverseBindingAdapter(attribute = "android:name", event = "android:nameAttrChanged")
public static String getName(MyEditText editText) {
return editText.getText().toString();
}
@BindingAdapter(value = "android:name", requireAll = false)
public static void setName(MyEditText editText, String setname) {
if (setname == null)
return;
if (name.equals(setname))
return;
name = setname;
editText.setText(setname + "");
}
@BindingAdapter(value = "android:nameAttrChanged")
public static void setNameListenering(MyEditText editText, final InverseBindingListener listener) {
if (listener != null) {
editText.setOnNameListener(new OnNameListener() {
@Override
public void onName() {
listener.onChange();
}
});
} else {
editText.setOnNameListener(null);
}
}
public void setName(String name) {
if (name != null && !name.equals("")) {
if (onNameListener != null) onNameListener.onName();
}
}
public MyEditText(Context context) {
super(context);
}
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private OnNameListener onNameListener;
public void setOnNameListener(OnNameListener onNameListener) {
this.onNameListener = onNameListener;
}
public interface OnNameListener {
void onName();
}
}
我们可以看到 使用InverseBindingAdapter,必须要和BindingAdapter相结合使用。
不说其他,一个自定义的属性,必然要为其提供一个set/get方法。
我们先来看看InverseBindingAdapter标签 对应的方法就是getXXX对应自定义属性
提供完了get方法,还需要提供一个set方法,于是乎用BindingAdapter标签来定义一个set方法
到目前位置,我们似乎还有个变量还没被使用,event对应的属性,之前也说了event的作用就是用来监听自定义属性是否改变。所以我们也需要为event定义一个BindingAdapter属性,setNameListenering方法就是第二个参数就是DataBinding为我们生成的监听.
大家可以看到为什么还要写个OnNameListener接口,因为InverseBindingListener也需要被调用。
InverseBindingMethod与InverseBindingMethods
我们先来看看它们的使用方法
@InverseBindingMethods({
@InverseBindingMethod(type = MyEditText.class,attribute = "android:name",event = "android:nameAttrChanged",method = "android:getName")
})
说白了 InverseBindingMethod与InverseBindingMethod就是对上面自定义的一个总结
- type 指的是自定义View的类
- attribute 就是自定义的属性
- event 上面已经解释,这里就不在多说
- method 其实就是对应get方法
在回顾上面,是不是有属性的get/set方法?这么一看,有众一目了然的感觉
自定义属性的双向绑定和非自定义属性的双向绑定,就这么讲解完了,若有错误的地方请指出,大家共同进步
网友评论