这边算是对mvvm的学习记录。大部分都来自网上!
先说下Data Binding的利弊
优势
DataBinding 出现以前,我们在实现 UI 界面时,不可避免的编写大量的毫无营养的代码:比如View.findViewById()
;比如各种更新 View 属性的 setter:setText()
,setVisibility()
,setEnabled()
或者setOnClickListener()
等等。
这些“垃圾代码”数量越多,越容易滋生 bug。
使用 DataBinding,我们可以避免书写这些“垃圾代码”。
劣势
使用 Data Binding 会增加编译出的 apk 文件的类数量和方法数量。
新建一个空的工程,统计打开 build.gradle 中 Data Binding 开关前后的 apk 文件中类数量和方法数量,类增加了 120+,方法数增加了 9k+(开启混淆后该数量减少为 3k+)。
如果工程对方法数量很敏感的话,请慎重使用 Data Binding。
1.基本MVVM使用
环境搭配
首先要让Android Studio使用DataBinding库才可以实现mvvm模式
在项目的主app项目Module内加入DataBinding引用
apply plugin: 'com.android.application'
android {
......
dataBinding{
enabled = true
}
......
}
还有一个注意的就是
- Android Studio版本在1.3以上
- gradle的版本要在1.5.0-alpha1以上
基础使用
在说使用之前我们需要将默认的xml布局的最外层加入layout嵌套
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!--这边设置data数据-->
......
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<!--这是原来的xml布局-->
........
</LinearLayout>
</layout>
我们创建如下布局
-
对TextView EditText Button之类的设置文本
先创建一个mvvmString类
public class mvvmString {
private String str1;
private String str2;
private String str3;
public mvvmString(String str1, String str2, String str3) {
this.str1 = str1;
this.str2 = str2;
this.str3 = str3;
}
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
}
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
public String getStr3() {
return str3;
}
public void setStr3(String str3) {
this.str3 = str3;
}
}
之后我们对xml布局进行设置
首先是data
<data>
<variable name="string" type="com.gjn.msdemo.mvvm.mvvmString" />
</data>
或者
<data>
<import type="com.gjn.msdemo.mvvm.mvvmString" />
<variable name="string" type="mvvmString" />
</data>
在data内加入variable属性设置名字和类型
之后我们就可以使用了,下面我们对TextView EditText Button分别设置
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{string.str1}" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@{string.str2}"
android:ems="10"
android:inputType="textPersonName" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{string.str3}" />
之后我们对TestActivity进行绑定数据和设置数据
public class TestActivity extends AppCompatActivity{
private ActivityTestBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_test);
binding.setString(new mvvmString("111","222","按钮"));
}
}
这边提下,在哪个xml下面绑定就会生成相应的Binding
好比这个 R.layout.activity_test 他的生成是按照_进行拼接形成ActivityTestBinding这个类
-
对ImageView设置图片
还是创建一个mvvmImage类
public class mvvmImage {
@BindingAdapter({"image"})
public static void imageLoader(ImageView imageView, String url){
Glide.with(imageView.getContext()).load(url).into(imageView);
}
}
这边用了Glide来加载图片
之后我们在data中加入新的类型
<data>
<import type="com.gjn.msdemo.mvvm.mvvmString" />
<variable name="string" type="mvvmString" />
<variable name="image" type="String" />
</data>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:image="@{image}" />
中间省略了 LinearLayout等布局
使用只需要在binding中加入设置的图片即可
String url = "http://7xi8d6.com1.z0.glb.clouddn.com/20180109085038_4A7atU_rakukoo_9_1_2018_8_50_25_276.jpeg";
binding.setImage(url);
-
对Button设置点击事件
还是先创建一个mvvmClick类
public class mvvmClick {
public void onClick(View view){
Toast.makeText(view.getContext(), "点击", Toast.LENGTH_SHORT).show();
}
}
之后设置data
<data>
<import type="com.gjn.msdemo.mvvm.mvvmString" />
<variable name="string" type="mvvmString" />
<variable name="image" type="String" />
<variable name="click" type="com.gjn.msdemo.mvvm.mvvmClick" />
</data>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{click.onClick}"
android:text="@{string.str3}" />
中间省略了 LinearLayout等布局
在Activity中的使用如下
binding.setClick(new mvvmClick());
点击了按钮就会弹出Toast
这样就算是绑定了基本的数据了
下面贴下完整的Activity和xml布局的代码
public class TestActivity extends AppCompatActivity{
private ActivityTestBinding binding;
private String url = "http://7xi8d6.com1.z0.glb.clouddn.com/" +
"20180109085038_4A7atU_rakukoo_9_1_2018_8_50_25_276.jpeg";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_test);
binding.setString(new mvvmString("111","222","按钮"));
binding.setImage(url);
binding.setClick(new mvvmClick());
}
}
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.gjn.msdemo.mvvm.mvvmString" />
<variable name="string" type="mvvmString" />
<variable name="image" type="String" />
<variable name="click" type="com.gjn.msdemo.mvvm.mvvmClick" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:image="@{image}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{string.str1}" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@{string.str2}"
android:ems="10"
android:inputType="textPersonName" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{click.onClick}"
android:text="@{string.str3}" />
</LinearLayout>
</layout>
2.MVVM进阶使用
-
BaseObservable的使用
让mvvmStrig继承BaseObservable并改写
public class mvvmString extends BaseObservable{
private String str1;
private String str2;
private String str3;
public mvvmString(String str1, String str2, String str3) {
this.str1 = str1;
this.str2 = str2;
this.str3 = str3;
}
@Bindable
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
notifyPropertyChanged(com.gjn.msdemo.BR.str1);
}
@Bindable
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
notifyPropertyChanged(com.gjn.msdemo.BR.str2);
}
@Bindable
public String getStr3() {
return str3;
}
public void setStr3(String str3) {
this.str3 = str3;
notifyPropertyChanged(com.gjn.msdemo.BR.str3);
}
}
即对get数据进行Bindabel,设置属性之后调用刷新方法notifyPropertyChanged
这时候修改数据就只要
mvvmString string = new mvvmString("111","222","按钮");
binding.setString(string);
string.setStr1("333");
string.setStr2("444");
string.setStr3("按钮2");
就可以修改了。否则如果没有继承BaseObservable的话,修改数据是需要
如下操作
mvvmString string = new mvvmString("111","222","按钮");
binding.setString(string);
string.setStr1("333");
string.setStr2("444");
string.setStr3("按钮2");
binding.setString(string);
就是重新再绑定一次,BaseObservable只是加入了刷新
但是BaseObservable这个方法还是太麻烦了,如果类多了。每个类都写一个M绑定...实在是累人
-
ObservableField的使用
创建新的mvvmString2
public class mvvmString2 {
public final ObservableField<String> str1 = new ObservableField<>();
public final ObservableField<String> str2 = new ObservableField<>();
public final ObservableField<String> str3 = new ObservableField<>();
}
修改 data使用
<data>
<import type="com.gjn.msdemo.mvvm.mvvmString2" />
<variable name="string" type="mvvmString2" />
<variable name="image" type="String" />
<variable name="click" type="com.gjn.msdemo.mvvm.mvvmClick" />
</data>
使用如下
mvvmString2 string2 = new mvvmString2();
binding.setString(string2);
string2.str1.set("text");
string2.str2.set("edit");
string2.str3.set("button");
这样对创建M也方便,并且设置也很快捷
-
BindingAdapter的使用
上面在修改图片的时候用到了BindingAdapter,这边就在来具体说下BindingAdapter的使用
首先我们先新建一个xml布局
act_test_adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="adapter" type="com.gjn.msdemo.mvvm.mvvmAdapter" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{adapter.add}"
android:text="add" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:texts="@{adapter.texts}" />
</LinearLayout>
</layout>
我们还创建了一个新的mvvmAdapter类
public class mvvmAdapter {
public ObservableArrayList<String> texts = new ObservableArrayList<>();
public mvvmAdapter(){
for (int i = 0; i < 5; i++) {
texts.add("text => " + i);
}
}
@BindingAdapter({"texts"})
public static void addTextView(LinearLayout linearLayout, ArrayList<String> texts){
linearLayout.removeAllViews();
for (String text : texts) {
TextView textView = new TextView(linearLayout.getContext());
textView.setText(text);
linearLayout.addView(textView);
}
}
public void add(View v){
texts.add("new text");
}
}
之后修改TestActivity
public class TestActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActTestAdapterBinding binding = DataBindingUtil.setContentView(this,R.layout.act_test_adapter);
binding.setAdapter(new mvvmAdapter());
}
}
实现效果如下
还有一些使用说明
-
自定义Binding名
首先我们创建的Bingding都是按照xml生成的,如果我们不希望这样生成可以修改data,在里面加入class属性
例如
<data class="NewBinding">
<variable name="adapter" type="com.gjn.msdemo.mvvm.mvvmAdapter" />
</data>
-
别名
可以再使用导包的时候设置别名,这样有利于区分一些差不多命名的包,就是在import 加入属性alias
<data class="NewBinding">
<import type="com.gjn.msdemo.mvvm.mvvmAdapter" alias="testadapter" />
<variable name="adapter" type="testadapter" />
</data>
-
在属性设置的时候要加入包
如果想设置一些属性
例如我想设置view的显示属性
稍微对上面的mvvmAdapter进行修改。这边就贴修改的代码
mvvmAdapter
public class mvvmAdapter {
public final ObservableField<Boolean> isView = new ObservableField<>();
}
xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data class="NewBinding">
<import type="com.gjn.msdemo.mvvm.mvvmAdapter" alias="testadapter" />
<import type="android.view.View" />
<variable name="adapter" type="testadapter" />
</data>
<!-- 其他布局-->
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{adapter.add}"
android:visibility="@{adapter.isView ? View.VISIBLE : View.GONE}"
android:text="add" />
<!-- 其他布局-->
</layout>
activity
public class TestActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ActTestAdapterBinding binding = DataBindingUtil.setContentView(this,R.layout.act_test_adapter);
NewBinding binding = DataBindingUtil.setContentView(this,R.layout.act_test_adapter);
mvvmAdapter adapter = new mvvmAdapter();
binding.setAdapter(adapter);
adapter.isView.set(false);
}
}