前言
目前主流的开发框架不外乎MVC、MVP和MVVM三种,对于前两种模式,我还算稍有了解。可是提到MVVM,在真正自己动手用过前,我真是一点概念都没有。使用了之后,我就深深爱上了MVVM。使用这种开发模式,可以大量减少令人头疼的id命名,自然也省去了许多TextView、Button变量的定义以及相关的findViewById()语句,编写表单页面时效果尤佳。
当然,最主要的还是Google在2015年时,已经为我们提供了DataBinding技术,我们可以非常方便地在Android Studio中使用这个框架进行MVVM模式的开发。官方的,好用的!
学习过程中我主要参考了以下两篇文章:
①玩转Android之MVVM开发模式实战,炫酷的DataBinding!
;
②Android-MVVM架构-Data Binding的使用
。
虽然网上资料也不少,但是有的内容学习时没找到,所以觉得需要补充一下。更是为了方便日后回顾,于是我写了这么一篇日志。看下面的内容前,可以先看一下第二个地址中文章开头的介绍,以便于了解为什么要使用MVVM模式。
一、使用前的准备
要在Android Studio中使用DataBinding,就需要先在gradle中进行一句简单的配置:
apply plugin: 'com.android.application'android { …… dataBinding { enabled = true }}dependencies { ……}
接着就可以开始了。
二、DataBinding在layout.xml中的使用
先看一下一个简单的布局
…… ……
界面效果如下:
可以看到,layout文件的根节点不再是一个View或者是一个ViewGroup,而是“layout”。在layout下又有一个<data>节点,在这个节点我们可以定义变量。下面的<ScrollView>就是以往我们使用的根节点啦,在<data>中定义的变量,可以在这里使用。变量类型可以像例子中那样写个import,也可以直接在variable下的type中定义完整路径。在花括号内,我们可以进行一些简单的计算。例如:
在这里,固定字符应使用“`”符号来区分,而不是引号。还可以进行三目运算,如:
“>”是“>”的意思,因为在xml中使用一些符号需要使用转义符,所以是这样写的,具体转移前面的有转载过。这里使用View的两个变量,是需要在<data>中进行声明的。
注意layout_width="match_parent"这种属性在layout节点也是可以写的,然后根ViewGroup中可以同时定义,都写上之后,该文件并不会报错,而在build时,与该文件关联的debug文件会报重复定义的错误。
三、DataBinding在.java文件中的使用
package example.chumi.demo.view.activity.mvvm;import android.databinding.DataBindingUtil;import android.support.v7.app.AppCompatActivity;import example.chumi.demo.R;import example.chumi.demo.model.User;public class MVVMTestActivity extends AppCompatActivity { private User user; private example.chumi.demo.databinding.ActivityMvvmtestBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// setContentView(R.layout.activity_mvvmtest); bindData(); } private void bindData() { binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvmtest); user = new User("Arcanite", "Magician", "奥金", 18); binding.setUser(user); }}
这里也有一个与以往有很大不同的地方——不再使用setContentView(),而是使用了
DataBindingUtil.setContentView()
通过这个方法我们可以获取到一个binding实例,使用这个实例可以对xml中定义的变量进行设置。binding的类型名由xml文件名决定,对比layout名和类名不难发现两者的关系。
这里需要提一下在Fragment中binding实例的获取,如果该Fragment对应的layout文件中包含变量,那么可以使用下面这种方法来获取。
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragemnt, container, false); binding = DataBindingUtil.bind(view); initData(); initView(view); return view;}
四、变量数据的同步更新
当实体类继承BaseObservable,就可以让框架帮助我们对界面上展示的数据进行更新。在需要更新的变量的set方法中增加notifyPropertyChanged()方法,在对应的get方法上添加声明“@Bindable”。下面是示例代码:
package example.chumi.demo.model;import android.databinding.BaseObservable;import android.databinding.Bindable;public class User extends BaseObservable { private String face; private String firstName; private String lastName; private String displayName; private int age; public User(String firstName, String lastName, String displayName, int age) { this.firstName = firstName; this.lastName = lastName; this.displayName = displayName; this.age = age; } public void setFace(String face) { this.face = face; notifyPropertyChanged(example.chumi.demo.BR.face); } @Bindable public String getFace() { return face; } @Bindable public String getFirstName() { return firstName; } @Bindable public String getLastName() { return lastName; } @Bindable public String getDisplayName() { return displayName; } @Bindable public int getAge() { return age; } public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(example.chumi.demo.BR.firstName); } public void setLastName(String lastName) { this.lastName = lastName; notifyPropertyChanged(example.chumi.demo.BR.lastName); } public void setDisplayName(String displayName) { this.displayName = displayName; notifyPropertyChanged(example.chumi.demo.BR.displayName); } public void setAge(int age) { this.age = age; notifyPropertyChanged(example.chumi.demo.BR.age); }}
如果BR总是报错,可以尝试在变量全部声明完毕后build一次工程。
五、layout.xml的几点补充
①Includes
name.xml 和 contact.xml都必须包含 <variable name="user" ../>
②事件的绑定
在第一部分中,增加点击事件监听器clickListener。
那么在ViewGroup部分,我们就可以直接使用这个监听器。
③自定义事件
在实体类中声明如下方法:
/** * * 对应的xml属性声明 app:load_img="@{food.imgUrl}",使用时需要在layout的声明中增加 xmlns:app="http://schemas.android.com/apk/res-auto" * 前面写定义的名字,后面填参数 * * @param img 需要显示图片的控件,DataBinding会自动传 * @param imgUrl 图片地址,需要在layout文件中的控件属性声明中写好 */@BindingAdapter("load_img")public static void loadInternetImage(ImageView img, String imgUrl) { Picasso.with(img.getContext()).load(imgUrl).into(img);}
对应的layout文件可以在添加注释中的声明后,在第一个参数对应的控件中使用这个方法。
有空时我会Demo程序上传到码云,届时将会贴上链接,希望大家多多指教。