目录
第二种:JavaBean中字段改为ObservableField
第三种:不使用JavaBean,创建Observable Clollections ;
配置
DataBinding的应用需要在android4.0(API14)及以上版本
首先需要在build.gradle文件中配置环境
android {
...
dataBinding {
enabled = true
}
}
简单使用
先生成java bean类,User
xml文件
正常DataBinDing绑定到xml中的数据格式是,以layout标签为根,首先添加data标签,绑定数据,代码如下:【代码来源】
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
activity
在Activity中DataBinding调用xml有两种方式,一种是通过DataBingdingUtil调用xml文件名:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User("Test", "User");
binding.setUser(user);
}
第二种是直接通过LayoutInflater隐式的生成:
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
1
第三种:如果View已经被生成,需要重新绑定到Binding时可以调用如下:
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
如果是在Fragment、ListView、Recyclerview中则可以通过以下两种方式调用:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
注意:DataBinding绑定xml并自动生成Bingding文件的名称,是根据你定义xml文件的文件名称自动生成的,例如:你定义了一个activity_main.xml的文件,那么自动绑定完成之后生成的Binding文件名就是ActivityMainBinding.java
高级使用
import type
<data>
<import type="com.test.qby.newtestapplication.model.TestModel"/>
\\别名
<import type="com.test.qby.newtestapplication.model.TestModel" alias="Model222"/>
<variable
name="test"
type="TestModel" />
</data>
布局文件中对data数据的引用
android:text="@{@string/test+test.name}"
@{TestModel.getName()},但是需要getName()为static方法
基本数据类型
<data>
<variable
name="array"
type="String[]" />
<variable
name="list"
type="java.util.ArrayList<String>" />
<variable
name="map"
type="java.util.HashMap<String,String>" />
<variable
name="arrayIndex"
type="int" />
<variable
name="listIndex"
type="int" />
<variable
name="key"
type="String" />
</data>
<LinearLayout
android:orientation="vertical"
style="@style/MathchMathch">
<TextView
style="@style/MatchWrap"
android:text="@{array[arrayIndex]}"/>
<TextView
style="@style/MatchWrap"
android:text="@{list[listIndex]}"/>
<TextView
style="@style/MatchWrap"
android:text="@{map[key]}"/>
</LinearLayout>
@{}包裹内容还支持运算符
“A??B”表示对前面字符串A是否为空的判断,如果为空,不显示,否则显示后面的字符串B,这里空判断包括null和字符串trim()之后为”“的判断。(就是说纯空格的字符串也是会作为空处理的)
<data>
<variable
name="str"
type="String"/>
<variable
name="error"
type="boolean"/>
</data>
<LinearLayout
style="@style/MatchMatch"
android:orientation="vertical">
<TextView
。。。
android:text='@{error ?"error": "没错"}'
。。。 />
<TextView
。。。
android:text='@{str ?? str}'
。。。 />
</LinearLayout >
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
事件绑定
方式1
<variable
name="testClick"
type="android.view.View.OnClickListener" />
...
//在控件中添加点击事件
android:onClick="@{testClick}"
代码调用
binding.setTestClick(view -> Log.e(TAG, "点击测试"));
方式2
导入处理类、引用处理类的方法
<variable
name="mHandler"
type="com.test.qby.newtestapplication.listener.MyHandler" />
...
android:onClick="@{view->mHandler.testClick(view)}"
方式3
android:onClick="@{mHandler::testClick}"
新建事件的处理类
/**
* Created by qby on 2018/1/30 0030.
* 事件处理类
*/
public class MyHandler {
private static final String TAG = "MyHandler";
public void testClick(View view){
Log.e(TAG,"点击测试");
}
}
在页面调用
binding.setMHandler(new MyHandler());
这样写,适用于多个页面点击事件执行代码一样的情况,因为执行代码是写在MyHandler的testClick中的。
如果想执行不同代码,可以重写方法
或者(如下)
2、将MyHandler改为接口
/**
* Created by qby on 2018/1/30 0030.
* 事件处理接口
*/
public interface MyHandler {
void testClick(View view);
}
在页面调用
binding.setMHandler(view -> Log.e(TAG, "点击测试"));
自定义属性
Databinding提供了@BindingAdapter(“属性名”)注解来完成自定义属性。
在JavaBean中定义如下方法:
@BindingAdapter("show")
public static void showIcon(ImageView iv, String imgUrl) {
if (!TextUtils.isEmpty(imgUrl)) {
Glide.with(iv)
.load(imgUrl)
.into(iv);
}
}
布局中引用时记得先加上自定义命名空间
xmlns:app="http://schemas.android.com/apk/res-auto"
在控件中使用
<ImageView
style="@style/WrapWrap"
app:show="@{test.url}"/>
代码调用
页面中调用方式也有几种
\\1
ActivityMainTestBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main_test);
\\2
View inflate = LayoutInflater.from(this).inflate(R.layout.activity_main_test, null);
ActivityMainTestBinding bind = DataBindingUtil.bind(inflate);
\\3
ActivityMainTestBinding inflate = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.activity_main_test, null, false);
数据刷新
第一种:JavaBean继承BaseObservable
public class TestModel extends BaseObservable{
private String name;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
}
JavaBean继承BaseObservable;getter方法添加@Bindable注解;settter方法内部添加通知notifyPropertyChanged;
第二种:JavaBean中字段改为ObservableField
Databinding提供的类包括:ObservableField,ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,ObservableInt,ObservableLong,ObservableFloat,ObservableDouble,ObservableParcelable。
public class TestModel {
public ObservableField<String> name = new ObservableField<>();
public ObservableBoolean name = new ObservableBoolean();
}
别忘了字段修饰符要public,调用直接使用set()
TestModel testModel = new TestModel();
testModel.name.set("这是测试文字");
testModel.isTrue.set(true);
布局中引用方式与其他的没区别,在代码中获取值就直接调用get()
String name = testModel.name.get();
boolean isTrue = testModel.isTrue.get();
这种方法虽然看着没有getter/setter方法,但是它是在内部实现过了。
第三种:不使用JavaBean,创建Observable Clollections ;
这种方式是为了不创建Javabean,而使用动态数据结构来更新UI。Databinding提供了ObservableArrayMap,ObservableArrayList两个类来实现。
使用代码动态创建
ObservableArrayList<Object> observableList = new ObservableArrayList<>();
observableList.add("databindingList");
observableList.add(12);
binding.setListIndex(1);
binding.setObservableList(observableList);
ObservableArrayMap<String,Object> observableMap = new ObservableArrayMap<>();
observableMap.put("hash1", "databindingMap");
observableMap.put("hash2", 16);
observableMap.put("hash3", "显示");
binding.setKey("hash3");
binding.setObservableMap(observableMap);
使用方式跟使用ArrayList、HashMap没什么区别
<data>
<variable
name="list"
type="android.databinding.ObservableArrayList<Object>" />
<variable
name="map"
type="android.databinding.ObservableArrayMap<String,Object>" />
<variable
name="listIndex"
type="int" />
<variable
name="key"
type="String" />
</data>
<LinearLayout
android:orientation="vertical"
style="@style/MathchMathch">
<TextView
style="@style/MatchWrap"
android:text="@{String.valueOf(list[listIndex])}"/>
<TextView
style="@style/MatchWrap"
android:text="@{String.valueOf(map[key])}"/>
</LinearLayout>
数据转换
自行转换
在布局中引用的数据,如果要求的类型与获取的类型不一致,需要开发者自行转换。
android:text=”要求是CharSequence类型”,使用String.valueOf()转换;
还有一种非常实用的转换:
<TextView
style="@style/WrapWrap"
android:text="@{String.valueOf(observableMap[key])}"
android:visibility="@{error?android.view.View.VISIBLE:android.view.View.INVISIBLE}"
android:textColor="@{error?@color/red:@color/green}"
android:background="@{error?@color/green:@color/red}"
/>
这里根据error是否为true,判断了TextView的显示隐藏、背景以及文字的颜色。
android:visibility中可以将android.view.View使用import导包,写成
//data节点下
<import type="android.view.View"/>
//引用
android:visibility="@{error?View.VISIBLE:View.INVISIBLE}"
注意同一属性只能引用同种类型的数据,即android:background中error为true/false,引用@color只能引用@color,引用@drawable只能引用@drawable,不能同时@color和@drawable混用(可能你混用还是能运行,但是一改变error的值切换的时候就会“嘣嘣嘣”了)。
自动转换
Databinding提供了自
目录
第二种:JavaBean中字段改为ObservableField
第三种:不使用JavaBean,创建Observable Clollections ;
动转换注解@BindingConversion,还是来个栗子:
布局:
<variable
name="time"
type="java.util.Date"/>
<TextView
style="@style/WrapWrap"
android:text="@{time}"/>
代码:
binding.setTime(new Date());
直接运行,当然会报错,关键在这:
/**
* Created by qby on 2018/1/31 0031.
* 自动转换器
*/
public class MyDateConverter {
@BindingConversion
public static String convertDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
return sdf.format(date);
}
}
列表适配器
ListView的适配器使用Databinding:
public class MyListViewAdapter<T> extends BaseAdapter {
private LayoutInflater inflater;
private int layoutId;
private int variableId;
private List<T> list;
public FutureWeatherAdapter(Context context, int layoutId, List<T> list, int resId) {
this.layoutId = layoutId;
this.list = list;
this.variableId = resId;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewDataBinding dataBinding;
if (convertView == null) {
dataBinding = DataBindingUtil.inflate(inflater, layoutId, parent, false);
} else {
dataBinding = DataBindingUtil.getBinding(convertView);
}
dataBinding.setVariable(variableId, list.get(position));
return dataBinding.getRoot();
}
}
dataBinding.getRoot()获取到的就是convertView;dataBinding.setVariable跟上面在页面写的binding.setTime一样,只不过setTime是因为已经转换成特定唯一的ActivityMainTestBinding,内部也是调用的setVariable,因为这个是公共ListView适配器,只能获取到抽象类ViewDataBinding,所以只能使用setVariable。variableId就相当于BR.time,这里与条目布局文件需要的对象一致。
例如 条目布局中:
<variable
name="test"
type="com.test.qby.newtestapplication.model.TestModel" />
RecyclerView的适配器
使用Databinding:
public class MyRecyclerViewAdapter<T> extends RecyclerView.Adapter {
private LayoutInflater inflater;
private int layoutId;
private int variableId;
private List<T> list;
public MyRecyclerViewAdapter(Context context, int layoutId, List<T> list, int resId) {
this.layoutId = layoutId;
this.list = list;
this.variableId = resId;
inflater = LayoutInflater.from(context);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
ViewDataBinding binding = DataBindingUtil.inflate(inflater, layoutId, viewGroup, false);
return new MyViewHolder(binding);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof MyViewHolder) {
MyViewHolder holder1 = (MyViewHolder) holder;
holder1.getBinding().setVariable(variableId, list.get(position));
holder1.getBinding().executePendingBindings();
}
}
@Override
public int getItemCount() {
return list.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public MyViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public ViewDataBinding getBinding() {
return this.binding;
}
}
}
Databinding在RecyclerView中的使用跟ListView中基本一样,出现了一个executePendingBindings方法是因为:
当数据改变时,binding会在下一帧去改变数据,如果我们需要立即改变,就去调用executePendingBindings方法。
在这里的作用就是让改变数据立即执行。