Android MVVM一步一步来实现

通过本文可以了解到的内容:
1.如何使用dataBinding;
2.设置点击事件(含参数的);
3.数据与UI同步更新;
4.基本的dataBinding语法;
5.自定义注解。

按照官网的文档一步一步来学习
官方文档传送门

简单开始

第一步,在module的build.gradle文件中添加如下代码,注意是android节点下添加:

android {
    ...
    dataBinding {
        enabled = true
    }
}

第二步,增加一个数据实体类。

public class UserBean{
    private String firstName;

    public UserBean(String firstName) {
        this.firstName = firstName;
    }
}

第三步,修改layout文件,以layout节点开始,data节点包含数据实体类,其他就和以前布局文件没有啥区别。
给某个textview绑定数据:android:text="@{user.firstName}",user就是某个实体类在这个文件中的引用名,firstName为这个实体类的某个属性。



    

        
        
        

        
    

    

        

        


    


第四步,在activity中的使用,ActivityMainBinding的命名规则是和其绑定的xml文件的命名规则相关的,例如,activity_main为xml文件的名称,那么就在其后面增加一个binding,首字母大写即可。

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserBean userBean = new UserBean("shuai bu shuai");
binding.setUser(userBean);
坑Num.1

注意ActivityMainBinding这个类,编译器不会自动编译出来,需要build一下项目这个类才能够被引用到。

坑Num.2
From the perspective of data binding, these two classes are equivalent. 

这是官网的一段话,我来断章取义一下,第一句话不知道怎么翻译,什么鬼从data binding的透明(还能穿透明装呢)?申明来看?(知道的朋友指点一下)第二句,这两个类是等同的。然后我就跟着官方文档一样就写了一个数据model的构造器。

public class UserBean{
    private String firstName;

    public UserBean(String firstName) {
        this.firstName = firstName;
    }
}

编译,啪!不出意外,你已经得到了ide的一个异常

Error:(31, 29) Could not find accessor com.hotelgg.android.hotelggreview
.data.UserBean.firstName 

应该添加一个get方法的入口,所以不知道官方文档说的这两者是等同的是嘛意思!

public class UserBean{
    private String firstName;

    public UserBean(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

}

添加点击事件

第一步,添加一个ClickHandler类,名字随便取。

public class ClickHandler {
    private BaseActivity context;
    private ActivityMainBinding mDataBinding;

    public ClickHandler(BaseActivity context, ActivityMainBinding mDataBinding) {
        this.context = context;
        this.mDataBinding = mDataBinding;
    }

    public void onClick(View view) {
        if (R.id.main_tv == view.getId()) {
            UserBean bean = mDataBinding.getUser();
            bean.setFirstName("perry");
        } else if (R.id.person_name == view.getId()) {
            //  这里先不用理会
            PersonBean bean = mDataBinding.getPerson();
            bean.name.set("mary");
        }

    }

}

第二步,在activity中添加绑定

binding.setOnclick(new ClickHandler(this, binding));

第三步,对应的xml中添加引用,上面已经给出完整代码。



        


// textview添加点击事件
android:onClick="@{onclick.onClick}"

然后,就没有然后了,可以运行开干了。

点击事件中传递参数

这种场景主要应用于:一个recyclerview的item在点击时需要取出当前item的数据,比如一个当前item的id或者url要传递到下一个页面中去。这个时候从绑定的数据中取出这个参数也有相应的办法。



        

        

        

    

// 某个控件的点击方法,其实就是lambda表达式的形式
android:onClick="@{()->itemClick.onClick(hotelInfo)}"

然后在adapter中进行相应的注册,这里涉及到在recyclerview当中使用,具体的代码会在后头贴出。这里只是简单的演示。

((ItemSingleTextBinding) binding).setItemClick(new UserAdapterItemClick());

public static class UserAdapterItemClick {
    public void onClick(HotelInfoBean bean) {
        LogUtil.e(bean.title.get());
        bean.title.set("change big hotel");
    }
}

这里有一点小小的观点需要表达一下:我们可以把这里的UI这样理解,它有自身的数据源model,有自身的操作行为handler,而同一个页面,我更加倾向于让其使用一个handler,这样复用性高一些,至于每个控件的数据源,我们可以通过传递进来的ActivityMainBinding对象进行获取和改变,UI层面就不需要做除了初始化之外的其他任何工作。所有的逻辑操作全由handler进行。

虽然点击事件和数据绑定都已经完成,但是数据绑定后,改变数据源是不是就能够直接让UI发生相应的更新变化呢?答案是否定的。

数据源与UI同步更新

如果想要改变数据源后同步更新UI界面,就需要对数据源进行一定的修改,我们还是以UserBean为例。

第一种方法
继承BaseObservable,然后在get方法上加上@Bindable注解,在set方法中加入notifyPropertyChanged(BR.firstName);进行数据更新。这个BR有点类似于资源文件引用中的R,也是包名.BR

public class UserBean extends BaseObservable{
    private String firstName;
    private int num;

    public UserBean(String firstName, int num) {
        this.firstName = firstName;
        this.num = num;
    }

    @Bindable
    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
        notifyPropertyChanged(BR.num);
    }

    @Bindable
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }
}

第二种方法
用Google给我们提供的Observable...类,支持所有数据类型。

public class PersonBean {
    public ObservableField name = new ObservableField<>();
    public ObservableField lastName = new ObservableField<>();
}

// 更新方式
personBean.lastName.set("White");

一些基本的语法

增加默认值
除了指定相应的绑定数据对象外,还可以增加默认值,如下:

android:text="@{user.firstName,default=test}"

数据类型转换
类型的转换,把int型转化成string类型并进行存储,其中int值还可以进行相应的计算。

android:text="@{String.valueOf(user.num + 1)}"

使用资源文件属性
还可以指定资源文件中的属性,并且直接使用占位符的形式来填充数据(这个功能强大)。

android:text="@{@string/name_format(person.name,person.lastName)}"

    第一个:%s,第二个:%s

使用三目运算符

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

不过注意,在导入集合类型时,咱们一般都会使用泛型类,以下泛型类为string类型,因为是基本数据类型,所以不用导入。使用泛型类的格式如下:


    
    

自定义注解

这个功能相当之强大,废话不多说,直接上代码。先做一个工具类,名字随便取,如我的AppBindingUtils,具体啥意思,注释当中已经说的很清楚。

public class AppBindingUtils {
    // BindingAdapter注解,自定义注解,可以让某些控件增加自定义属性,并且,只要在xml中出现该属性,
    // 就会调用该注解注释的方法。
    // 其中 bind_data 为xml中自定义属性的属性名,setDataTest这个名字随便取。
    // 不让recyclerView每次都设置adapter,这样会刷新多余数据
    @BindingAdapter("bind_data")
    public static void setDataTest(RecyclerView recyclerView, ObservableArrayList data) {
        if (recyclerView != null && recyclerView.getAdapter() == null) {
            recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext(), LinearLayoutManager.VERTICAL, false));
            recyclerView.setAdapter(new UserAdapter(data));
        }
    }
}

xml布局文件中如下,这样,当编译器发现了xml中有一项自定义注解编注的属性时,就会带着这个属性去找有@BindingAdapter注解编注过并且属性名为bind_data的方法,并在初始化该控件的时候,调用这个被注解标注过的方法。




在activity当中设置这个数据源。

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
beans = new ObservableArrayList<>();
beans.add(new MultipleListBean(1, bean));
beans.add(new MultipleListBean(1, bean1));
binding.setData(beans);

以上介绍差不多就是dataBinding的一些基础内容的介绍了。欢迎留言讨论。

你可能感兴趣的:(Android MVVM一步一步来实现)