公司项目最近已经用MVP模式重新构造完成,重构后的项目直观明了,代码复用性高,易于调试维护。
之后主管让我去了解MVC,MVP ,MVVM,Data Binding相关的知识,于是便有了下面这篇博客。参考资料:
这里很感谢这两篇文章的理论与技术支持,感谢这些大神。
OK,开始今天的主题。
关于MVP模式与MVVM模式孰是孰非,我想不是我一个初学者短短几句就能够说清楚的,我们也不用纠结于此。引用大神的话“真正的最佳实践都是人想出来的”。我们为何不结合一下MVP和MVVM的特点:MVP+Data Binding,依旧使用presenter去做和model层的通信,同时使用data binding去轻松的bind data。
Data Binding—-2015年的Google IO 大会上,Android 团队发布的一个 数据绑定框架。以后可以直接在 layout 布局 xml 文件中绑定数据,无需再通过findViewById或者注解框架去设置数据。
具体看看MVP+Data Binding在项目中的应用:
点击button,请求加载网络数据,两秒过后,将模拟的数据显示在textView中。
1.准备工作
新建一个 Project,确保 Android 的 Gradle 插件版本不低于 1.5.0-alpha1:
classpath ‘com.android.tools.build:gradle:1.5.0‘
然后修改对应模块(Module)的 build.gradle:
dataBinding {
enabled true
}
2.数据对象
这里的数据对象有两种情况,一种是普通的数据对象,一种是绑定的数据对象,能够自动更新数据。
先看第一种:
/**
* Created by tangyangkai on 16/4/27.
*/
public class User{
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
一个简单的User,两个属性以及它的 getter 和 setter。
3.布局文件
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.tangyangkai.myapplication.User"></variable>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="getWebMsg"
android:text="模拟请求网络数据" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓氏:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="名字:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}" />
</LinearLayout>
</layout>
(1.)data节点:使用 Data Binding 之后,xml 的布局文件就不再用于单纯地展示 UI 元素,还需要定义 UI 元素用到的变量。所以,它的根节点不再是一个 ViewGroup,而是变成了 layout,并且新增了一个节点 data。data 节点的作用就像一个桥梁, 把数据(Model)与 UI(View) 进行绑定,搭建了 View 和 Model 之间的通路。
(2.)申明variable:
<data>
<variable
name="user"
type="com.example.tangyangkai.myapplication.User"> </variable>
</data>
在 xml 布局文件的 data 节点中声明一个 variable,这个变量会为 UI 元素提供数据(例如 TextView 的 android:text),然后在 Java 代码中把『后台』获取的数据与这个 variable 进行绑定。其中 type 属性就是我们在 Java 文件中定义的 User 类。
(3.)使用variable:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
数据与 Variable 绑定之后,xml 的 UI 元素就可以直接使用了。
4.Activity实现
public class FiveActivity extends AppCompatActivity implements StringView {
private StringPresenter mStringPresenter;
ActivityFiveBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStringPresenter = new StringPresenter(this);
binding = DataBindingUtil.setContentView(
this, R.layout.activity_five);
}
//请求网络数据
public void getWebMsg(View view) {
mStringPresenter.SetUrl();
}
@Override
public void ShowString(String firstName, String lastName) {
User user = new User();
user.setFirstName(firstName);
user.setLastName(lastName);
binding.setUser(user);
}
}
(1.)ActivityFiveBinding是框架自动生成的, 其中的set 方法也是根据 variable 名称而生成的。
(2.)修改 FiveActivity 的 onCreate 方法,用 DatabindingUtil.setContentView() 来替换掉 setContentView()
(3.)创建一个 User 对象,将从网络获取的数据通过set方法传递给User,通过 binding.setUser(user) 与 variable 进行绑定。
至此,使用第一种数据对象的data binding流程已经过了一遍,其实核心就是申明variable,绑定variable,使用variable。第二种数据对象也一样,只不过是绑定variable时候有些不一样。
5.第二种数据类型
/**
* Created by tangyangkai on 16/4/27.
*/
public class User extends BaseObservable{
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
@Bindable
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
(1.)要实现 数据对象的绑定,Android 原生提供了已经封装好的一个类 - -BaseObservable,并且实现了监听器的注册机制,我们只需要继承 BaseObservable。
(2.)BR 是编译阶段生成的一个类,功能与 R.java 类似,用 @Bindable 标记过 getter 方法会在 BR 中自动生成一个 entry。
(3.)当数据发生变化时会调用 notifyPropertyChanged(BR.firstName) 方法,通知系统 BR.firstName 这个 entry 的数据已经发生变化,再去更新 UI。
public class FiveActivity extends AppCompatActivity implements StringView {
private StringPresenter mStringPresenter;
User user = new User();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStringPresenter = new StringPresenter(this);
ActivityFiveBinding binding = DataBindingUtil.setContentView(
this, R.layout.activity_five);
binding.setUser(user);
}
//请求网络数据
public void getWebMsg(View view) {
mStringPresenter.SetUrl();
}
@Override
public void ShowString(String firstName, String lastName) {
user.setFirstName(firstName);
user.setLastName(lastName);
}
}
与第一种数据对象不同的是,variable的数据绑定会在初始化的时候就完成。获取网络数据成功以后,只要直接传递给user即可,数据以及UI的更新会在内部进行。
6.MVP的代码
Presenter层
/**
* Created by tangyangkai on 16/4/11.
* Presenter作为中间层,持有View和Model的引用,对model层的数据进行处理,控制View层的展示
*/
public class StringPresenter implements StringModel.getMsgListener {
private StringView stringview;
private StringModel stringmodel;
public StringPresenter(StringView stringview) {
this.stringview = stringview;
stringmodel = new StringModel();
}
//对view层提供请求数据方法
public void SetUrl() {
stringmodel.getWebMsg(this);
}
//将model层返回的数据传递给view
@Override
public void getMsgSuccess(String firstName, String lastName) {
stringview.ShowString(firstName, lastName);
}
}
Model层
/**
* Created by tangyangkai on 16/4/11.
* 模拟请求数据,具体实战根据返回结果处理
*/
public class StringModel {
public interface getMsgListener {
void getMsgSuccess(String firstName, String lastName);
}
public void getWebMsg(getMsgListener listener) {
//模拟网络数据请求
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
listener.getMsgSuccess("tang", "yangkai");
}
}
之前一篇博客关于MVP的使用说的很清楚,这里就不详细解释了。
关于Data Binding的高级用法以及注意事项,开始那两篇文章介绍的很详细,大家可以细细品尝。
关于这些模式与框架,仁者见仁,智者见智,其实都是为了尽量降低程序的耦合性和提高代码的复用性。以上是记录自己最近学习这些框架模式的一些心得,有不当之处欢迎大家指出,一起进步。
公司开新项目了,准备投入新的战斗O(∩_∩)O~