请选择 进入手机版 | 继续访问电脑版
设为首页收藏本站

houcx

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 819|回复: 0

Android DataBinding完全解析

[复制链接]

216

主题

218

帖子

1089

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1089
发表于 2018-1-5 11:15:13 | 显示全部楼层 |阅读模式
前言
       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_paddingdimen/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}"  
  •     androidnClick="@{handle::Click}"/>  

[java] view plain copy


  • <!--  
  •      2、监听绑定:使用()组,括号里面所填的是你为参数起的名字,这样你就可以在后面的括号进行引用  
  •         如果你监听的事件需要返回值,那么你的方法也要返回一个相同类型  
  • -->  
  • <Button  
  •      android:layout_width="match_parent"  
  •      android:layout_height="wrap_content"  
  •      androidnClick="@{()->handle.eventHandler(user)}"  
  •      android:text="传入布局文件中的数据" />  
  •   
  • <Button  
  •      android:layout_width="match_parent"  
  •      android:layout_height="wrap_content"  
  •      androidnClick="@{(thisView)->handle.eventHandlerView(thisView,user)}"  
  •      android:text="传入此View" />  
  • <!--如果你需要为一个点击事件设置一个断言,那么使用void作为一个标志,表示什么也不做-->  
  • <Button  
  •      android:layout_width="match_parent"  
  •      android:layout_height="wrap_content"  
  •      androidnClick="@{(v)->handle.isVisible(v)?handle.doSomething():void}"  
  •      android:text="判断是否为visible" />  
  •   
  • <!--对于一些控件有自己专门的单击事件,需要创建下面的属性进行避免  
  •     SearchView    androidnSearchClick  
  •     ZoomControls  androidnZoomIn  
  •     ZoomControls  androidnZoomOut-->  
  • <SearchView  
  •      android:layout_width="match_parent"  
  •      android:layout_height="wrap_content"  
  •      androidnClick="@{(v)->handle.searchClick(v)}"  
  •      androidnSearchClick="@{(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/reddrawable/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/redcolor/green}" />  


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




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|houcx ( 京ICP备15004793号  

GMT+8, 2019-1-24 10:18 , Processed in 0.079568 second(s), 26 queries .

Powered by houcx! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表