Android DataBinding完全解析

    xiaoxiao2021-03-26  22

    前言

           2015年的Google IO大会上,Android 团队发布了一个数据绑定框架(Data Binding Library),官方原生支持 MVVM 模型。以后可以直接在 layout 布局 xml 文件中绑定数据了,无需再 findViewById然后手工设置数据了。其语法和使用方式和 JSP 中的 EL 表达式非常类似。

    配置:

    [java]  view plain  copy   android {       ....       dataBinding {           enabled = true       }   }   首先我们肯定是先要和布局文件进行绑定

    [java]  view plain  copy   ActivityLayoutDetailBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_layout_detail);  

    替换掉setContentView即可,ActivityLayoutDetailBinding这个类是自动生成的和你的布局文件名字一样,如果你想要去改变名字的话

    [java]  view plain  copy   <layout xmlns:android="http://schemas.android.com/apk/res/android"       xmlns:bind="http://schemas.android.com/apk/res-auto">          <!--这里你也可以为Binding类进行命名,有三种形式           1、Custom:会在databinding包下           2、.Custom:会在当前的包名下创建           3、com.andly.Custom:会在指定的包名下进行创建-->       <data class="Custom">   接下来就是在布局文件里面引用代码中的对象

    Part 1、先是声明类和对象

    [java]  view plain  copy   <data>         <!--java.lang.*包是自动导入的不需要你再次的导入-->         <import type="android.view.View" />         <!--当出现了import相同的类名的时候需要为其指定外号alias加以区分-->         <import             alias="CustomView"             type="com.andly.administrator.andlydatabinding.myview.View" />            <import type="com.andly.administrator.andlydatabinding.entry.User" />         <import type="com.andly.administrator.andlydatabinding.event_handing.EventHandler" />         <import type="java.util.List" />         <import type="java.util.Map" />         <import type="android.graphics.drawable.Drawable" />            <variable             name="image"             type="Drawable" />            <variable             name="note"             type="String" />            <variable             name="boo"             type="boolean" />            <variable             name="view"             type="View" />         <!--这里不能用<>来表示泛型,要使用< >-->         <variable             name="list"             type="List<User>" />            <variable             name="strList"             type="List<String>" />            <variable             name="map"             type="Map<String,String>" />   Part 2、引用对象中的方法和值

          1、简易的引用

    [java]  view plain  copy   <TextView       android:layout_width="match_parent"       android:layout_height="wrap_content"       android:text="@{note}"/>           2、集合引用

    [java]  view plain  copy   <!--使用Map这种Key Value的格式数据时           格式1"@{map['key']}"           格式2'@{map["key"]}'           格式3"@{map[`key`]}"  反引号   -->   <TextView       android:layout_width="match_parent"       android:layout_height="wrap_content"       android:text="@{map[`one`]}" />   这时我们在代码中调用binding的set方法便可以为其设置内容了

    [java]  view plain  copy   List<User> users = new ArrayList<>();   User u = new User();   u.setName("List User Name Data");   users.add(u);   binding.setList(users);      Map map = new HashMap();   map.put("one""Map One Data");   binding.setMap(map);   在引用的同时也可以加上简单的逻辑运算

    [java]  view plain  copy   <!--数据绑定将自动检测null异常,如果你的表达式为null,它将会给它赋值为(null)    如果为int类型则默认为0-->    <!--之前都是写三元运算符的形式,当然在数据绑定中也能够使用,但更推荐下面那种-->    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="@{boo?note:null}" />    <!--?? :null合并运算符,当左边为null会显示右边-->    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="@{note??null}"        android:textColor="#00FF00"        android:textSize="18sp" />   [java]  view plain  copy   <!--引用资源文件-->   <ImageView       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:paddingLeft="@{boo?@dimen/large_padding:@dimen/small_padding}"       android:src="@{image}" />   [java]  view plain  copy   <TextView       android:layout_width="match_parent"       android:layout_height="wrap_content"       android:text='@{String.valueOf(map[`one`])}'/>   等等,详情请看源码

    然而上面引用的数值并不能随着用户的操作动态的改变,如果你想动态的改变需要使用Observable前缀的类,如:

    [java]  view plain  copy   //使用绑定的集合   /**   * 如果要使用ObservableArrayMap类需要在layout里面导入 <import type="android.databinding.ObservableMap" />   */   user = new ObservableArrayMap<>();   user.put(Fields.FIRST_NAME, "Google");   user.put(Fields.LAST_NAME, "Inc.");   user.put(Fields.AGE, 17);   binding.setObserableMap(user);   对象的话需要继承BaseObservable并设置你索要观察的字段 [java]  view plain  copy   public class DataUser extends BaseObservable {       //继承BaseObservable类,想对谁进行监听则需要在get方法上面添加@Bindable注解,在set方法里面使用notifyPropertyChanged       //注意到的是在这个方法里面要传入BR.name参数,这是Binding类为该字段生成唯一变量进行绑定       private String name;       private int age;       private String info;       @Bindable       public String getName() {           return name;       }          public void setName(String name) {           this.name = name;           notifyPropertyChanged(BR.name);       }          public int getAge() {           return age;       }       public void setAge(int age) {           this.age = age;       }          public String getInfo() {           return info;       }       public void setInfo(String info) {           this.info = info;       }   }  

    当你去执行小的工作的时候,想去节省时间或者减少配置可以使用ObservableField或者其兄弟

    [java]  view plain  copy   //它们内部包含Observable对象,使用时要去创建public final类型的   //为其赋值和取值操作:user.firstName.set("Google");  int age = user.age.get();   public final ObservableField<String> name = new ObservableField<>();   public final ObservableInt age = new ObservableInt();   public final ObservableField<String> info = new ObservableField<>();  

    Part 3、添加事件处理方法

    [java]  view plain  copy   <!--       绑定事件的格式有两种:         1、方法引用:直接用handle.Click或者handle::Click  推荐后者   -->   <Button       android:layout_width="match_parent"       android:layout_height="wrap_content"       android:text="@{user.name}"       android:onClick="@{handle::Click}"/>   [java]  view plain  copy   <!--        2、监听绑定:使用()组,括号里面所填的是你为参数起的名字,这样你就可以在后面的括号进行引用           如果你监听的事件需要返回值,那么你的方法也要返回一个相同类型    -->    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="@{()->handle.eventHandler(user)}"        android:text="传入布局文件中的数据" />       <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="@{(thisView)->handle.eventHandlerView(thisView,user)}"        android:text="传入此View" />    <!--如果你需要为一个点击事件设置一个断言,那么使用void作为一个标志,表示什么也不做-->    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="@{(v)->handle.isVisible(v)?handle.doSomething():void}"        android:text="判断是否为visible" />       <!--对于一些控件有自己专门的单击事件,需要创建下面的属性进行避免       SearchView    android:onSearchClick       ZoomControls  android:onZoomIn       ZoomControls  android:onZoomOut-->    <SearchView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="@{(v)->handle.searchClick(v)}"        android:onSearchClick="@{(v)->handle.onSearchClick(v)}">       </SearchView>   实现监听方法,保证参数个数、类型、返回值都要保证和你使用set时监听一样,不然就出报错。

    [java]  view plain  copy   public void checkChanged(View view, boolean isCheck) {       System.out.println("checkChanged:" + view + "    " + isCheck);   }      public boolean longClick(View view) {       System.out.println("longClick:" + view);       return true;   }  

    (沉璧浮光补充,https://github.com/ConnorLin/DataBindingDemo)二者主要区别在于方法调用在编译时处理,而监听绑定于事件发生时处理。

    Part 4、在布局中使用include

    如果你需要用到从xml传过来的数据需要去使用bind:user属性,这里的user是你定义的实体类名

    [java]  view plain  copy   <!--当你使用include的时候,你可以使用命名空间和属性中的变量名   来将数据传送到另一个布局中去,值得注意的是当include的父节点为merge时将不支持-->   <include       layout="@layout/detail_include"       bind:user="@{user}" />  

    然后只需要在include布局里面声明之后便可以直接使用了。

    Part 5、在布局中使用ViewStub

    [java]  view plain  copy       /**       * 为ViewStub设置监听,当显示的时候为它绑定数据,因为当不显示的ViewStub会在视图中消失       */       vs = (ViewStub) findViewById(R.id.viewstub);       vs.setOnInflateListener(new ViewStub.OnInflateListener() {           @Override           public void onInflate(ViewStub stub, View inflated) {               ViewstubBinding viewstubBinding = ViewstubBinding.bind(inflated);               Info info = new Info();               info.setInfo("Andly Info");               viewstubBinding.setInfo(info);               Drawable d = getResources().getDrawable(R.mipmap.ic_launcher);               viewstubBinding.setDrawable(d);           }       });      }      public void toggleViewStub(View view) {       vs.inflate();   }  

    Part 6、在布局中使用RecycleView控件

              1、添加RecycleView控件

    [java]  view plain  copy   <!--       这里使用到了自定义属性,因为RecycleView里面有setAdapter方法,所以这里可以直接用app:adapter   -->   <android.support.v7.widget.RecyclerView       android:id="@+id/rv"       android:layout_width="match_parent"       android:layout_height="match_parent"       app:adapter="@{adapter}" />               2、为RecycleView定义适配器

    [java]  view plain  copy   @Override   public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {       ViewDataBinding viewDataBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutId, parent, false);       return new ViewHolder(viewDataBinding);   }   @Override   public void onBindViewHolder(ViewHolder holder, int position) {       holder.binding.setVariable(variable, list.get(position));       holder.binding.executePendingBindings();       //当然这里你也可以为其设置点击如:       //holder.binding.getRoot.setOnclickListener()   }   @Override   public int getItemCount() {       return list.size();   }      class ViewHolder extends RecyclerView.ViewHolder {       ViewDataBinding binding;          public ViewHolder(ViewDataBinding binding) {           super(binding.getRoot());           this.binding = binding;       }   }                   3、为RecycleView设置Adapter

    [java]  view plain  copy   //这里注意的是一定要是BR.dataInfo不能是其它的常数   MyAdapter adapter = new MyAdapter(list, R.layout.rv_item, BR.dataInfo);   binding.setAdapter(adapter);   binding.rv.setLayoutManager(new LinearLayoutManager(this));   这样就大功告成,然而在很多情况我们都需要去对每个Item进行处理,如显示网络图片等等,这里我们就需要使用数据绑定自定义属性的功能,看代码

    [java]  view plain  copy   <ImageView       android:layout_width="150dp"       android:layout_height="90dp"       app:imageError="@{@drawable/android}"       app:imagePath="@{dataInfo.imageUrl}" />   [java]  view plain  copy   //当你在一个方法只需要一个参数的时候可以使用@BindingAdapter("imageUrlStr"),加上之后就可以在布局文件中直接使用imageUrlStr   //运行之后就会调用loadImage方法   @BindingAdapter("imageUrlStr")   public static void loadImage(ImageView iv, String url) {       Glide.with(iv.getContext()).load(url).into(iv);//这里使用Glide库   }      //上面是为loadImage传入一个参数,当传入两个或多个参数的时候应使用@BindingAdapter({"imagePath", "imageError"})   //这个的ImageView自定义了两个属性一个是imagePath传入的是url,一个是imageError为Drawable   @BindingAdapter({"imagePath""imageError"})   public static void downloadImage(ImageView iv, String url, Drawable error) {       Glide.with(iv.getContext()).load(url).error(error).into(iv);   }  

    上面的方法使用的是静态方法,如果你不想使用静态方法你需要重写一个数据绑定组件类去实现DataBindingComponent

    [java]  view plain  copy   public class MyComponent implements android.databinding.DataBindingComponent {       private Utils utils;       @Override       public Utils getUtils() {           if (utils == null) {               utils = new Utils();           }           return utils;       }   }   然后你需要在Activity为其进行设置

    [java]  view plain  copy   //第一种方式   DataBindingUtil.setDefaultComponent(new MyComponent());   //第二种方式   ActivityMyListViewBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_my_list_view,new MyComponent());   //第三种方式   DataBindingUtil.bind(root,new MyComponent());  

    最后有个不起眼的小功能,就是当使用数据绑定的时候在预览界面不能看到显示的内容,这时你可以为你的控件设置默认显示内容

    [java]  view plain  copy   android:text="@{placeName,default=PLACEHOLDER}"  

    注意:

    不允许使用混合类型

    [java]  view plain  copy   <!--值得注意的是       android:background="@{boo?@color/red:@drawable/background}"       这么写将会发生错误,因为在BindingConversion默认实现为:       @BindingConversion       public static ColorDrawable convertColorToDrawable(int color) {               return new ColorDrawable(color);           }   -->   <ImageView       android:layout_width="100dp"       android:layout_height="100dp"       android:layout_marginTop="20dp"       android:background="@{boo?@color/red:@color/green}" />  

    源码下载:http://download.csdn.NET/detail/weiwozhiyi/9644657

    转载请注明原文地址: https://ju.6miu.com/read-658937.html

    最新回复(0)