Android应用Android从入门到放弃

DataBinding使用总结

2020-04-03  本文已影响0人  小耗子_20da

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、转义字符

<   &lt;
>   &gt;
&   &amp;
“  &quot;
‘  &apos;
//&quot;包裹的字符串内容中,有些字符可能会报错,如中文冒号,最好少用
android:text="@{&quot;转义&quot;+@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>
上一篇下一篇

猜你喜欢

热点阅读