Android DataBinding完全解析

前言

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


配置:

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

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

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

[java]  view plain  copy
 
  1. "http://schemas.android.com/apk/res/android"  
  2.     xmlns:bind="http://schemas.android.com/apk/res-auto">  
  3.   
  4.       
  5.     class="Custom">  
接下来就是在布局文件里面引用代码中的对象

Part 1、先是声明类和对象

[java]  view plain  copy
 
  1.   
  2.         
  3.       <import type="android.view.View" />  
  4.         
  5.       <import  
  6.           alias="CustomView"  
  7.           type="com.andly.administrator.andlydatabinding.myview.View" />  
  8.   
  9.       <import type="com.andly.administrator.andlydatabinding.entry.User" />  
  10.       <import type="com.andly.administrator.andlydatabinding.event_handing.EventHandler" />  
  11.       <import type="java.util.List" />  
  12.       <import type="java.util.Map" />  
  13.       <import type="android.graphics.drawable.Drawable" />  
  14.   
  15.       
  16.           name="image"  
  17.           type="Drawable" />  
  18.   
  19.       
  20.           name="note"  
  21.           type="String" />  
  22.   
  23.       
  24.           name="boo"  
  25.           type="boolean" />  
  26.   
  27.       
  28.           name="view"  
  29.           type="View" />  
  30.         
  31.       
  32.           name="list"  
  33.           type="List" />  
  34.   
  35.       
  36.           name="strList"  
  37.           type="List" />  
  38.   
  39.       
  40.           name="map"  
  41.           type="Map" />  
Part 2、引用对象中的方法和值

      1、简易的引用

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

[java]  view plain  copy
 
  1.   
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="wrap_content"  
  4.     android:text="@{map[`one`]}" />  
这时我们在代码中调用binding的set方法便可以为其设置内容了

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

[java]  view plain  copy
 
  1.   
  2.    
  3.  
  4.      android:layout_width="match_parent"  
  5.      android:layout_height="wrap_content"  
  6.      android:text="@{boo?note:null}" />  
  7.    
  8.  
  9.      android:layout_width="match_parent"  
  10.      android:layout_height="wrap_content"  
  11.      android:text="@{note??null}"  
  12.      android:textColor="#00FF00"  
  13.      android:textSize="18sp" />  
[java]  view plain  copy
 
  1.   
  2.     android:layout_width="wrap_content"  
  3.     android:layout_height="wrap_content"  
  4.     android:paddingLeft="@{boo?@dimen/large_padding:@dimen/small_padding}"  
  5.     android:src="@{image}" />  
[java]  view plain  copy
 
  1.     android:layout_width="match_parent"  
  2.     android:layout_height="wrap_content"  
  3.     android:text='@{String.valueOf(map[`one`])}'/>  
等等,详情请看源码

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

[java]  view plain  copy
 
  1. //使用绑定的集合  
  2. /** 
  3.  * 如果要使用ObservableArrayMap类需要在layout里面导入  
  4.  */  
  5. user = new ObservableArrayMap<>();  
  6. user.put(Fields.FIRST_NAME, "Google");  
  7. user.put(Fields.LAST_NAME, "Inc.");  
  8. user.put(Fields.AGE, 17);  
  9. binding.setObserableMap(user);  
对象的话需要继承BaseObservable并设置你索要观察的字段
[java]  view plain  copy
 
  1. public class DataUser extends BaseObservable {  
  2.     //继承BaseObservable类,想对谁进行监听则需要在get方法上面添加@Bindable注解,在set方法里面使用notifyPropertyChanged  
  3.     //注意到的是在这个方法里面要传入BR.name参数,这是Binding类为该字段生成唯一变量进行绑定  
  4.     private String name;  
  5.     private int age;  
  6.     private String info;  
  7.     @Bindable  
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.   
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.         notifyPropertyChanged(BR.name);  
  15.     }  
  16.   
  17.     public int getAge() {  
  18.         return age;  
  19.     }  
  20.     public void setAge(int age) {  
  21.         this.age = age;  
  22.     }  
  23.   
  24.     public String getInfo() {  
  25.         return info;  
  26.     }  
  27.     public void setInfo(String info) {  
  28.         this.info = info;  
  29.     }  
  30. }  

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

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

Part 3、添加事件处理方法

[java]  view plain  copy
 
  1.   
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="wrap_content"  
  4.     android:text="@{user.name}"  
  5.     android:onClick="@{handle::Click}"/>  
[java]  view plain  copy
 
  1.   
  2.  
  3.      android:layout_width="match_parent"  
  4.      android:layout_height="wrap_content"  
  5.      android:onClick="@{()->handle.eventHandler(user)}"  
  6.      android:text="传入布局文件中的数据" />  
  7.   
  8.  
  9.      android:layout_width="match_parent"  
  10.      android:layout_height="wrap_content"  
  11.      android:onClick="@{(thisView)->handle.eventHandlerView(thisView,user)}"  
  12.      android:text="传入此View" />  
  13.    
  14.  
  15.      android:layout_width="match_parent"  
  16.      android:layout_height="wrap_content"  
  17.      android:onClick="@{(v)->handle.isVisible(v)?handle.doSomething():void}"  
  18.      android:text="判断是否为visible" />  
  19.   
  20.    
  21.  
  22.      android:layout_width="match_parent"  
  23.      android:layout_height="wrap_content"  
  24.      android:onClick="@{(v)->handle.searchClick(v)}"  
  25.      android:onSearchClick="@{(v)->handle.onSearchClick(v)}">  
  26.   
  27.    
实现监听方法,保证参数个数、类型、返回值都要保证和你使用set时监听一样,不然就出报错。

[java]  view plain  copy
 
  1. public void checkChanged(View view, boolean isCheck) {  
  2.     System.out.println("checkChanged:" + view + "    " + isCheck);  
  3. }  
  4.   
  5. public boolean longClick(View view) {  
  6.     System.out.println("longClick:" + view);  
  7.     return true;  
  8. }  

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

Part 4、在布局中使用include

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

[java]  view plain  copy
 
  1.   
  2.     layout="@layout/detail_include"  
  3.     bind:user="@{user}" />  

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

Part 5、在布局中使用ViewStub

[java]  view plain  copy
 
  1.     /** 
  2.      * 为ViewStub设置监听,当显示的时候为它绑定数据,因为当不显示的ViewStub会在视图中消失 
  3.      */  
  4.     vs = (ViewStub) findViewById(R.id.viewstub);  
  5.     vs.setOnInflateListener(new ViewStub.OnInflateListener() {  
  6.         @Override  
  7.         public void onInflate(ViewStub stub, View inflated) {  
  8.             ViewstubBinding viewstubBinding = ViewstubBinding.bind(inflated);  
  9.             Info info = new Info();  
  10.             info.setInfo("Andly Info");  
  11.             viewstubBinding.setInfo(info);  
  12.             Drawable d = getResources().getDrawable(R.mipmap.ic_launcher);  
  13.             viewstubBinding.setDrawable(d);  
  14.         }  
  15.     });  
  16.   
  17. }  
  18.   
  19. public void toggleViewStub(View view) {  
  20.     vs.inflate();  
  21. }  

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

          1、添加RecycleView控件

[java]  view plain  copy
 
  1.   
  2.     android:id="@+id/rv"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     app:adapter="@{adapter}" />  
            2、为RecycleView定义适配器

[java]  view plain  copy
 
  1. @Override  
  2. public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  3.     ViewDataBinding viewDataBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutId, parent, false);  
  4.     return new ViewHolder(viewDataBinding);  
  5. }  
  6. @Override  
  7. public void onBindViewHolder(ViewHolder holder, int position) {  
  8.     holder.binding.setVariable(variable, list.get(position));  
  9.     holder.binding.executePendingBindings();  
  10.     //当然这里你也可以为其设置点击如:  
  11.     //holder.binding.getRoot.setOnclickListener()  
  12. }  
  13. @Override  
  14. public int getItemCount() {  
  15.     return list.size();  
  16. }  
  17.   
  18. class ViewHolder extends RecyclerView.ViewHolder {  
  19.     ViewDataBinding binding;  
  20.   
  21.     public ViewHolder(ViewDataBinding binding) {  
  22.         super(binding.getRoot());  
  23.         this.binding = binding;  
  24.     }  
  25. }  
                3、为RecycleView设置Adapter

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

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

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

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

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

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

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

注意:

不允许使用混合类型

[java]  view plain  copy
 
  1.   
  2.     android:layout_width="100dp"  
  3.     android:layout_height="100dp"  
  4.     android:layout_marginTop="20dp"  
  5.     android:background="@{boo?@color/red:@color/green}" />  

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

你可能感兴趣的:(架构篇)