Android MVVM架构模式(二)——DataBinding框架(一)

DataBinding框架(一)

用途:

  1. 去掉Activity或Fragment中的处理UI的逻辑代码
  2. XML成为UI唯一的真实来源
  3. 不再使用findViewById(cached)
    优势:
    1 性能高 安全
    2 保证执行在主线程

类似方案:
ButterKnife
android annotation
RoBoBinding


简单的数据绑定

1 添加依赖
app的gradle中的android中:

dataBinding {
    enabled = true
}

2 在XML中,在layout根节点下(没有则创建) 并创建< data>标签

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
   <!--(后面写Variable)-->
    </data>
 <!-- 其他界面布局以及组件-->
 // 这里一定注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 // 后面的这部分 相当于是之前普通的布局 **要有一个大的布局类型** 里面才是小的布局、组件
    <LinearLayout>
    ....
    </LinearLayout>
</layout>

3 创建POJO类 (或javaBean类)

public class User {
    private final String firstName;
    private final String lastName;
public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
	   public String getFirstName() {
	       return this.firstName;
	   }
	    public String getLastName() {
	        return lastName;
	    }
   }
   

4 定义 Variable

<data>
	  <variable
        name="User"
        <!-- 包下的User类的对象-->
        type="com.scnu.sihao.testmvvm.User"/>
        <!-- 也可以用String类型,下面的//*****后的与这里对应-->
         <variable name="firstName" type="String" />
	    <variable name="lastName" type="String" />
</data>

在data内描述了一个名为user的变量属性,使其可以在这个layout中使用
其中 type 属性就是我们在 Java 文件中定义的 User 类

也可以采用import形式
要具体到类 不能用 *

<data>
    <import type="com.example.User" />
    <variable name="user" type="User" />
</data>

如果导入不同的包中有相同的类名,使用import 中的 alias 属性

<import type="com.example.home.data.User" />
<!--别名为DetailUser-->
<import type="com.examle.detail.data.User" alias="DetailUser" /> 
<variable name="user" type="DetailUser" />


**5 编译之后,插件会根据 xml 的命名(activity_main),在project->app->build-> output会生成ActivityMainBinding类,并且该类继承ViewDataBinding **
将对应XML的首字符大写,其他单词首字符也大写,然后加上Binding
记得要编译,ActivityMainBinding类才会出现

app\build\generated\source\apt\debug\com\scnu\sihao\sinaweibodemo\databinding
中可以找到

也可以在< data >标签中自定义class名


<data class=“XXXXXX”
...
...>
</data>

6 绑定 Variable
修改MainActivity中的onCreate,用 DatabindingUtil.setContentView() 来替换掉 setContentView(),然后创建一个 user 对象,通过 binding.setUser(user) 与 variable 进行绑定

// import android.databinding.DataBindingUtil;
// import 包名.databinding.ActivityMainBinding;
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        User user = new User("testFirst", "testLast");
        binding.setUser(user);    // 根据XML的variable的name值,setUser方法自动生成
      //或者通过
      // binding.setVariable();
        //**************************与上面的string对应
        binding.setFirstName("自定义值");//根据XML的variable的name值,setFirstName方法自动生成
        binding.setLastName("自定义值");//根据XML的variable的name值,setLastName方法自动生成

如果使用的 ListView 或者RecyclerView可以使用这个

 ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);

ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

有时候不能预先知道 Bingding 类的种类,这时候可以使用DataBindingUtil 类

ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
    parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);


7 可以在XML中使用Variable了

android:text="@{user.firstName}"
android:text="@{firstName}"
android:text="@{lastName}"

使用 通过Binding.setXXX()自动生成的方法传值给Bean类,Bean类再通过set方法分字段,XML中的通过对象.XXX去调用getXXX方法,去获得值。

不支持的语法:

this
super
new
其他都类似java 

在Activity中,创建Databinding连接XML,然后setViewModel()连接model
在ViewModel中 进行数据处理和事件监听


至此,一个简单的数据绑定就完成了


8 事件绑定

- 方法绑定

在XML中

<variable
        name="clickListener"
        type="com.scnu.sihao.testmvvm.MyClickListener" />
        <!-- 使用创建的包下的点击事件类-->

创建MyClickListener

 public void myOnClick(View view){
        Toast.makeText(view.getContext(),"onclick",Toast.LENGTH_SHORT).show();
        Log.i("onclick","onclick is ok!");
    }
    public boolean myOnLongClick(View view){
        Toast.makeText(view.getContext(),"onLongclick",Toast.LENGTH_SHORT).show();
        Log.i("onLongclick","onLongclick is ok!");
        return true;
    }

接着在XML中的按钮中引用

 android:onClick="@{clickListener::myOnClick}" 
 android:onLongClick="@{clickListener::myOnLongClick}"
 //或者
 android:onClick="@{clickListener.myOnClick}" 
 android:onLongClick="@{clickListener.myOnLongClick}"

最后 在 onCreate 中

mybinding.setClickListener(new MyClickListener());

-监听器绑定:
lambda 表达式
可以传参数到代码中

创建方法 参数为从XML中得到的数据

  public void myClickListener(User user){
        Log.i("myclickListener",user.getFirstName());
    }

XML中使用

<!-- 传入user参数-->
 android:onClick="@{() -> clickListener.myClickListener(user)}"

9 使用类方法

  • 普通方法的绑定,直接在布局文件中,通过变量名调用方法即可,和java代码里面使用相似

android:text="@{user.getName(user.firstName, user.lastName)}


10 自动数据绑定

直接修改数据对象并不能直接更新 UI,Android的Data Binding模块给提供了通知机制,有3种类型,分别对应于类(Observable),字段(ObservableField),集合类型(Observable Collections)

  • Observable(类)
    思想步骤:
    1 XML中定义包下的类对象
<variable
name="user"
type="com.scnu.sihao.User"/>

2 创建Bean类继承BaseObservable,然后在getXX方法上加入@Bindable注解

3 在Apdater或Activity中,通过binding.setUser(new User(AAA) ) 传入对象给XML的User(创建对象时可以传递参数AAA)

4 在Bean(User)中 setXX中获取这个参数并且针对不同字段的需要获取AAA的具体值,AAA.XXX,并且set中写notifyChange()方法 去自动同步更新
再在getXXX中retrun this.XXX就好了

5 也可以new Bean(AAA) AAA这里直接传数值,然后get去get就好了

例子:

// 第一步 继承一个BaseObservable
public class User extends BaseObservable {
    private String firstName;
    private String lastName;
// 第二步 加上Bindable注释    
    @Bindable
    public String getFirstName() {
        return this.firstName;
    }
     public void setFirstName(String firstName) {
        this.firstName = firstName;
// 第三步 当调用setFirstName函数时 自动调用notify去更新UI显示的数据
        notifyPropertyChanged(包名.项目名.BR.firstName);
    }
    @Bindable
    public String getLastName() {
        return this.lastName;
    }
   
    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(包名.项目名.BR.lastName);
    }
}

@Bindable注解是为了在编程的时候生成 BR 类,Bindable会在 BR 类中生成一个域变量 ,来表明这个域有没有被改变。通过代码可以看出,当数据发生变化时还是需要手动发出通知。 通过调用 notifyPropertyChanged(BR.firstName) 可以通知系统 BR.firstName 这个 entry 的数据已经发生变化,需要更新 UI。


  • 普通字段绑定:
    见:
    http://blog.csdn.net/weixin_37577039/article/details/78725838
    的第二点
    注意这里不需要set和get方法 因为set get方法是针对Bean的对象的

  • ObservableFields(字段)
    -具体到成员变量,这种方式无需继承 BaseObservable
    -如果变量比较少,都是简单的数据类型时,可以用ObservableFields
    ObservableFields 自包含具有单个字段的observable对象。它有所有基本类型和一个是引用类型。要使用它需要在data对象中创建public final字段

private static class User extends BaseObservable {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}
// 对这些初始化值 也是使用 set和get操作

//当firstName、lastName变化时,UI 会得到通知,使用的赋值语句为
user.firstName.set("Google");
int age = user.age.get();

在ViewModel中

//在相应的逻辑处理中,也是使用setAge,setFirstName去修改参数



  • Observable Collections(集合类型)
    一些app使用更多的动态结构来保存数据。Observable集合允许键控访问这些data对象。ObservableArrayMap用于键是引用类型,如String。
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
//当 key 是 inter 是, ObservableArrayList 比较适用
ObservableArrayList< Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);

在layout文件中,通过String键可以访问map

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap<String, Object>"/>
</data>
     …
     <!-- 通过key直接访问-->
<TextView
   android:text='@{user["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
   
// 当key是int
<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList<Object>"/>
</data>
…
<TextView
   android:text='@{user[Fields.LAST_NAME]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

  • 立即绑定
    变量或Observable改变后,在下一帧才进行改变,如果需要立即执行,则可以通过
executePendingBindings();

11 带 ID 的 View

Data Binding 有效降低了代码的冗余性,甚至完全没有必要再去获取一个 View 实例,但是情况不是绝对的,万一我们真的就需要了呢?不用担心,只要给 View 定义一个 ID,Data Binding 就会为我们生成一个对应的 final 变量。

<TextView
    android:id="@+id/firstName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

上面代码中定义了一个 ID 为 firstName的 TextView,那么它对应的变量就是
public final TextView firstName; 就可以在java代码中使用这个firstName了
比如可以通过 binding.firstName.setText();赋值 (一般不这么用吧?)


12 ViewStubs

xml 中的 ViewStub 经过 binding 之后会转换成 ViewStubProxy, 具体代码可参考 ViewStubActivity.java

简单用代码说明一下,xml 文件与之前的代码一样,根节点改为 layout,在 LinearLayout 中添加一个 ViewStub,添加 ID。

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout
        ...>
        <ViewStub
            android:id="@+id/view_stub"
            android:layout="@layout/view_stub"
            ... />
    </LinearLayout>
</layout>

在 Java 代码中获取 binding 实例,为 ViewStubProy 注册 ViewStub.OnInflateListener 事件:

binding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
	@Override
	public void onInflate(ViewStub stub, View inflated) {
		ViewStubBinding binding = DataBindingUtil.bind(inflated);
		User user = new User("fee", "lang");
		binding.setUser(user);
	}
});

13 通过 @{} 可以直接把 Java 中定义的属性值赋值给 xml 属性

< TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

POJO:
普通java类 ,但不允许有业务方法,也不能携带有connection之类的方法
Java Bean :
可重用组件,类必须是具体的和公共的,并且具有无参数的构造器,有get,set的方法


inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById()的区别,inflate是加载一个布局文件,而findViewById则是从布局文件中查找一个控件。


ViewStub
ViewStub使用的是惰性加载的方式,即使将其放置于布局文件中,如果没有进行加载那就为空,不像其它控件一样只要布局文件中声明就会存在


Databinding结合RecyclerView和ImageView使用,以及Databinding的表达式,如数组字段,资源引用,参考下一篇博客:
http://blog.csdn.net/weixin_37577039/article/details/78725838

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值