DataBinding使用总结
1、Layout文件中修改根布局
按 alt + enter -->convert to data binding layout
图片1.png
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View"/> //导包
<import type="java.lang.String"/>
<variable
name="dataInfo" //变量名
type="com.example.lqf.databinding.DataInfo"/> //类名
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.constraint.ConstraintLayout>
</layout>
2、activity或fragment 或recyclerview中使用
//在activity中使用
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setDataInfo(new DataInfo("imageurl", "title", "info"));
//在fragment中使用
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_room,
container, false);
return mBinding.getRoot();
}
//在recyclerview的adapter中使用
@NonNull
@Override
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemXxxBinding binding = DataBindingUtil.inflate( LayoutInflater.from(parent.getContext()), R.layout.item_xxx, parent, false);
return new Holder(binding);
}
3、databean编写
①、继承BaseObservable类,在get方法前加注解 @Bindable,在set方法中加入 notifyPropertyChanged(BR.title);或 notifyChange();方法。如果是用public修饰的字段,则直接在字段前加注解 @Bindable。
public class DataInfo extends BaseObservable {
private String imageUrl;
private String title;
private String info;
@Bindable
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
//只更新本字段
notifyPropertyChanged(BR.title);
//更新所有字段
// notifyChange();
}
}
②不继承BaseObservable类,而是直接用 ObservableInt、 ObservableChar等类代替基本数据类型;用ObservableField<String>代替string类型(但是现在我还不知道怎么与json互转)
补充:快速生成getset方法
如果每次生成getset方法后都要手动修改,这种体力活不是懒惰的程序员的作风,所以在网上找了不少插件,结果要么报错,要么点击没有反应,这里分享下我自己的做法,不需要插件,只要添加一个自己的getset模板就阔以了。
1、快捷键 Alt+Insert →选择get and set;
2、点击 ... 按钮:
选择getset模板
3、添加get或set方法:
添加自己的getset模板
4、右边文本框中添加以下代码:
①Get
@Bindable
#if($field.modifierStatic)
static ##
#end
$field.type ##
#set($name = $StringUtil.capitalizeWithJavaBeanConvention($StringUtil.sanitizeJavaIdentifier($helper.getPropertyName($field, $project))))
#if ($field.boolean && $field.primitive)
is##
#else
get##
#end
${name}() {
return $field.name;
}
②Set
#set($paramName = $helper.getParamName($field, $project))
#if($field.modifierStatic)
static ##
#end
void set$StringUtil.capitalizeWithJavaBeanConvention($StringUtil.sanitizeJavaIdentifier($helper.getPropertyName($field, $project)))($field.type $paramName) {
#if ($field.name == $paramName)
#if (!$field.modifierStatic)
this.##
#else
$classname.##
#end
#end
$field.name = $paramName;
}
5、将getset生成器修改成自己的就好了:
使用getset模板
3.1、kotlin下实体类
class User :BaseObservable(){
@get:Bindable
var name:String = ""
set(value) {
field = value
notifyPropertyChanged(BR.name)
}
@get:Bindable
var age:Int = 0
set(value) {
field = value
notifyPropertyChanged(BR.age)
}
}
这里有个坑,就是必须在build.gradle上面加下面插件,否则BR总是报错
apply plugin: 'kotlin-kapt'
4、BindingAdapter
@BindingAdapter({"image"})
public static void setImage(ImageView view, String url) {
Glide.with(view).load(url).into(view);
}
//layout中直接可以使用image传参
<ImageView
...
image='@{dataInfo.imageUrl,default = "@mipmap/ic_launcher_round"}'
/>
/**
* 覆盖ImageView的src方法
* 方法名随便取,重点是BindingAdapter括号中的名字,一定要和View的属性名一样,而且签名需要加上android,如ImageView的src属性,前面加上android:就可以覆盖imageview的src属性了
*
* @param view 被操作的View
* @param url 图片路径
*/
@BindingAdapter({"android:src"})
public static void setSrc(ImageView view, String url) {
Glide.with(view).load(url).apply(new RequestOptions().circleCropTransform()).into(view);
}
//layout中就可以替换掉View原有属性
<ImageView
...
android:src='@{dataInfo.imageUr"}'
"/>
5、@BindingConversion 类型转换器
/**
* string转drawable
* 方法名真的不重要,自己看得懂就行
*
* @param str
* @return
*/
@BindingConversion
public static Drawable convertStringToDrawable(String str) {
if (str.equals("红色")) {
return new ColorDrawable(Color.parseColor("#FF4081"));
}
if (str.equals("蓝色")) {
return new ColorDrawable(Color.parseColor("#3F51B5"));
}
return new ColorDrawable(Color.parseColor("#344567"));
}
@BindingConversion
public static int convertStringToColor(String str) {
if (str.equals("红色")) {
return Color.parseColor("#FF4081");
}
if (str.equals("蓝色")) {
return Color.parseColor("#3F51B5");
}
return Color.parseColor("#344567");
}
//layout中使用
<TextView
...
android:background='@{"红色"}'
/>
6、链式调用
//比如上一个View定义了一个backgronun属性,下一个View也具有相同的属性,则可以这样使用
<TextView
android:id="@+id/tv_title"
...
android:background='@{"红色"}'
/>
<TextView
//直接使用上一个View的background,注意:①只有后面的View才能用前面的View,顺序反了可能会编译不过;②注意命名规范,如上一个View的id为tv_title,我们引用它的属性是需要写成tvTitle
android:background="@{tvTitle.background}"
/>
7、设置默认参数
<TextView
//预览界面在没有数据的情况下是一片空白,可以增加默认值
//目前DataBinding暂不支持使用@mipmap下的图片。
android:text='@{dataInfo.info,default="这个是默认值"}'
/>
8、双向绑定
<EditText
//双向绑定只要在@后加一个“=”号即可,常用在EditText控件上
android:text="@={dataInfo.info}"
/>
注意:如果我们在另一个控件中显示该值,这时候两个控件的值是可以同步改变的,但是,如果另一个控件是一个表达式,表达式中引用了该值,这时候两个控件的内容是不能同步的
<!-- TextView中的值会随着EditText的值改变而改变-->
<EditText android:text="@={cartInfo.count}" />
<TextView android:text="@{cartInfo.count}" />
<!-- TextView中的值不会随着EditText的值改变而改变-->
<EditText android:text="@={cartInfo.count}" />
<TextView android:text="@{ BigDecimalUtils.mul(goodsInfo.price, goodsInfo.count, 2)}" />
<!-- 解决方案-->
<!-- 在TextView其他属性中引用一下cartInfo.count,比如hint属性-->
<EditText android:text="@={cartInfo.count}" />
<TextView
android:hint="@{cartInfo.count}"
android:text="@{cartInfo.count}" />
9、静态方法调用(如使用工具类)
//比如需要调用TestUtils 中的test方法
public class TestUtils {
public static String test(String s) {
return s + "_hello";
}
}
//①引包,一定要记得,要不编译不通过找原因找到你想哭
<import type="com.example.lqf.databinding.TestUtils"/>
//②然后就可以使用了,和java没什么区别
<Button
android:text='@{TestUtils.test(dataInfo.info),default = "删除"}'
/>
10、事件监听与非静态方法调用
貌似非静态方法只能在监听事件里调用,实测在text属性中调用编译不了
/**
* 事件监听
* 一般会在activity里做一个内部类
*/
public class OnListener {
/**
* 方法调用,方法签名可以是任意的
*/
public void add() {
DataInfo dataInfo = new DataInfo(images[Math.abs(i) % images.length], "新增" + i, "爱就像蓝天白云,晴空万里,突然暴风雨");
mDataInfos.add(0, dataInfo);
i++;
mDataBinding.rvList.scrollToPosition(0);
}
/**
* 点击事件,形参一定要与点击事件的已知,方法名可以任意
* 这里响应的是onClick(View view)方法
*
* @param view
*/
public void remove(View view) {
mDataInfos.remove(0);
i--;
}
}
最后--------一定要记得set进去
mDataBinding.setOnListener(new OnListener());
//layout文件中
一定要记得这步骤
<variable
name="onListener"
type="com.example.lqf.databinding.MainActivity.OnListener"/>
①调用方法,可以使用lambda表达式
<Button
...
android:onClick="@{()->onListener.add()}"
/>
②
<Button
...
android:onClick="@{onListener.remove}"
//android:onClick="@{onListener::remove}"//或者写成这样
/>
11、include中使用
//item_xxxm.xml中
<variable
name="dataInfo"
type="com.example.lqf.databinding.DataInfo"/>
<include
layout="@layout/item_xxx"
app:dataInfo="@{dataInfo}"
/>
12、ViewStub(和include差不多)
注意:DataBinding不支持使用merge节点。
<ViewStub
...
android:id="@+id/view_stub"
android:layout="@layout/item_xxx"
app:dataInfo="@{dataInfo}"/>
//java代码中需要显示时
if (!mDataBinding.viewStub.isInflated()) {
mDataBinding.viewStub.getViewStub().inflate();
}
13、recyclerview使用
recyclerview还是原来的recyclerview,只是adapter需要做一定的修改
RecyclerView Adapter 与DataBinding的封装
//ViewHolder需要修改
class Holder extends RecyclerView.ViewHolder {
private ItemXxxBinding mItemXxxBinding;
//①将原来的形参 View itemView 改为 Binding
public Holder(@NonNull ItemXxxBinding itemXxxBinding) {
//②super中需要返回itemView,所以需要传入Binding.getRoot()
super(itemXxxBinding.getRoot());
mItemXxxBinding = itemXxxBinding;
}
}
//在recyclerview的adapter中使用
@NonNull
@Override
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemXxxBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_xxx, parent,
false);
return new Holder(binding);
}
@Override
public void onBindViewHolder(@NonNull Holder holder, int position) {
holder.mItemXxxBinding.setDataInfo(mDataInfos.get(position));
}
数据变化自动更新列表
/**
* 动态更新列表
* 当ObservableList数据变化时动态更新ui
*
* @param <T>
*/
public class DynamicChangeCallback<T> extends
ObservableList.OnListChangedCallback<ObservableList<T>> {
private RecyclerView.Adapter adapter;
public DynamicChangeCallback(RecyclerView.Adapter adapter) {
this.adapter = adapter;
}
@Override
public void onChanged(ObservableList<T> sender) {
adapter.notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(ObservableList<T> sender, int positionStart, int itemCount) {
adapter.notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeInserted(ObservableList<T> sender, int positionStart, int itemCount) {
adapter.notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeMoved(ObservableList<T> sender, int fromPosition, int toPosition, int itemCount) {
adapter.notifyItemRangeRemoved(fromPosition, itemCount);
adapter.notifyItemRangeInserted(toPosition, itemCount);
}
@Override
public void onItemRangeRemoved(ObservableList<T> sender, int positionStart, int itemCount) {
adapter.notifyItemRangeRemoved(positionStart, itemCount);
}
}
ObservableArrayList<DataInfo> mDataInfos ;//集合必须是Observablexxx
mDataInfos.addOnListChangedCallback(new DynamicChangeCallback<MyAdapter>(this));
14、表达式语法
支持的表达式
数学计算: + - / * %
字符串连接: +
逻辑: && ||
二进制: & | ^
一元运算符: + - ! ~
位移: >> >>> <<
比较: == > < >= <=
instanceof
组: ()
文字: - 字符,字符串,数字, null
类型转换
函数调用
字段存取
数组存取 []
三目运算符 ?:
不支持的表达式
this
super
new
显式泛型调用 <T>
15、Null合并运算符
//非null时选择左边的操作,反之选择右边的操作:
android:text='@{dataInfo.info??dataInfo.title}'
//等价于
android:text='@{dataInfo.title!=null?dataInfo.info:dataInfo.title}'
16、集合
//通用的集合类(arrays, lists, sparse lists, maps),可以使用[ ]操作符来存取:
private ArrayList<String> infos;//javabean中的arrayList类型
android:text="@{dataInfo.infos.get(0)}"
android:text="@{dataInfo.infos[0]}"
//以上两个方法得到的结果是一样一样的
17、资源
//具体什么回事,我也没搞懂
<string name="full_name">%1$s %2$s</string>
android:text="@{@string/full_name(dataInfo.info, dataInfo.title)}"
部分资源的表达式引用和普通引用有所不同:
Type Normal Reference Expression Reference
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList
18、转义字符
< <
> >
& &
“ "
‘ '
//"包裹的字符串内容中,有些字符可能会报错,如中文冒号,最好少用
android:text="@{"转义"+@string/full_name(dataInfo.info, dataInfo.title)}"
19、测试
//①创建抽象类
public abstract class BindingAdapterBase {
//如果EditText使用了双向绑定,这个方法使用很危险
@BindingAdapter("android:text")
public abstract void setText(TextView view,String value);
@BindingAdapter("android:src")
public abstract void setSrc(ImageView view, String url);
}
//②创建调试类,并继承BindingAdapterBase
public class DebugBindingAdapter extends BindingAdapterBase {
@Override
public void setText(TextView view, String value) {
view.setText("测试版:");
}
@Override
public void setSrc(ImageView view, String url) {
Glide.with(view).load(url).apply(new RequestOptions().circleCropTransform()).into(view);
}
}
//②创建正式版BindingAdapter,并继承BindingAdapterBase
public class ReleaseBindingAdapter extends BindingAdapterBase {
@Override
public void setText(TextView view, String value) {
view.setText("正式版:"+value);
}
@Override
public void setSrc(ImageView view, String url) {
Glide.with(view).load(url).into(view);
}
}
//③创建DataBindingComponent的实现类
public class DebugComponent implements DataBindingComponent {
//androidstudio无法自动生成,需要手敲或者复制粘贴
public BindingAdapterBase getBindingAdapterBase() {
return new DebugBindingAdapter();
}
}
public class ReleaseComponent implements DataBindingComponent {
public BindingAdapterBase getBindingAdapterBase() {
return new DebugBindingAdapter();
}
}
//④在MainActivity中调用
DataBindingUtil.setDefaultComponent(new DebugComponent());
//⑤如果使用静态方法,可以在形参中增加 DataBindingComponent component
@BindingAdapter("image")
public static void setImage(android.databinding.DataBindingComponent component, ImageView view, String url){
Glide.with(view).load(url).apply(new RequestOptions().circleCropTransform()).into(view);
}
*坑:
如果第①步中覆盖了view原有属性(如 @BindingAdapter("android:text")),则②③④都要走完;如果没有覆盖view原有属性(如:@BindingAdapter("text")),则没有关系。当然,如果某个类被layout文件引用了,而这个类里面也有@BindingAdapter("android:text")方法,
貌似也是可以跑通的。如:
public class DataInfo extends BaseObservable {
...
@BindingAdapter({"android:text"})
public static void setText(TextView view, String url) {
view.setText(url);
}
}
layout文件中:
<data>
...
<variable
name="dataInfo"
type="com.example.lqf.databinding.DataInfo"/>
</data>