欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MVC、MVP的区别和MVVM设计模式及实例

程序员文章站 2024-03-15 19:38:30
...


    MVVM简介                MVC和MVP的区别


可能你使用过 MVP 设计模式来对代码进行解耦, 但是当前谷歌发布 Data Binding 库来更加简化我们的代码 , 也催生了 MVVM 设计模式在 Android 中的引用 . 在 MVP 中, 我们需要 Model 、 View 、 Presenter 三样进行配合使用 , 但是在 View 中 还是会出现大量的类似 ShowLoad 之类的代码 . 而这次的 MVVM 是由 Model , View , ViewModel 进行配合的 . 其中的区别主要在于 ViewModel , Data Binding 的奇妙之处在于可以将 XML 文件与指定的 JAVA 类绑定 , 实现数据自动更新的效果 .下面来看看结构图:


MVC、MVP的区别和MVVM设计模式及实例


MVC、MVP的区别和MVVM设计模式及实例


在 MVVM 设计模式中 , 对于 View 中的数据更新操作都是通过 Data Binding 框架实现的 , 在 Model 发生变化时做出反映 .看看效果图:


MVC、MVP的区别和MVVM设计模式及实例


依然跟 MVP 的实例一样是一个查询 IP 地址归属地的 Demo .


首先需要打开 Data Binding 绑定功能 , 在 APP 的 build.gradle 文件中添加如下部分


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

先来看 XML 文件的变化


<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">
  <data>
    <variable
        name="viewModel"
        type="com.shire.mvvmdemo.viewModel.MainViewModel"
        />
  </data>
  <RelativeLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:paddingBottom="@dimen/activity_vertical_margin"
      android:paddingLeft="@dimen/activity_horizontal_margin"
      android:paddingRight="@dimen/activity_horizontal_margin"
      android:paddingTop="@dimen/activity_vertical_margin"
      >

    <EditText
        android:id="@+id/et_ip"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:hint="IP地址"
        />
    <Button
        android:id="@+id/btn_search"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_toEndOf="@id/et_ip"
        android:onClick="@{viewModel.search}"
        android:text="查询"
        />
    <TextView
        android:id="@+id/tv_msg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/et_ip"
        />
    <ProgressBar
        android:id="@+id/pb_load"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btn_search"
        android:layout_toStartOf="@id/btn_search"
        android:visibility="gone"
        style="?android:attr/progressBarStyleSmall"
        />
  </RelativeLayout>
</layout>


可以看到 根节点用的是 <layout> 标签 , 并且还出现了一个 <data> 标签 , 在 <data> 标签中还有一个 <variable> 标签 , 在 <variable> 标签中就是我们需要绑定的类以及他的名字 .


这里使用的名字为 viewModel , 绑定的类是 MainViewModel . 至于绑定这个有什么用, 下面会说到 .


再来看看 Activity 文件

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setViewModel(new MainViewModel(binding));
    }
}

你没看错 , 就是只有这几行代码 ! 其中的 ActivityMainBinding 类是 Data Binding 框架为我们自动生成的 , 这与你的 XML 文件名字相关 , 比如我的 XML 文件名字是 activity_main 那么生成的类就会取消下划线并且在最后加上 Binding 就得到了 ActivityMainBinding . 这个类的实例可以通过 DataBindingUtil.setContentView 来得到 . 这个类里面有我们 XML 文件里所有的控件信息 . 由此 , 也再也不需要去 findViewById 了! 我们先调用了 setViewModel 方法来设置 ViewModel 并且传进去了这个 Binding , 然后我们进入这个 ViewModel 来看看吧 . 界面上的管理目前基本可以全部转移到 ViewModel 中了.

public class MainViewModel implements onSearchListener {

  private ActivityMainBinding binding;
  private SearchModel searchModel = new SearchModel();
  private Handler handler;

  public MainViewModel(ActivityMainBinding binding) {
    this.binding = binding;
    handler = new Handler(Looper.getMainLooper());
  }

  public void search(View view) {
    binding.pbLoad.setVisibility(View.VISIBLE);
    searchModel.getIPaddressInfo(binding.etIp.getText().toString().trim(), this);
  }

  @Override public void onSuccess(final IPAddress ipAddress) {
    handler.post(new Runnable() {
      @Override public void run() {
        binding.pbLoad.setVisibility(View.GONE);
        binding.tvMsg.setText(ipAddress.toString());
      }
    });
  }

  @Override public void onError() {

    handler.post(new Runnable() {
      @Override public void run() {
        binding.pbLoad.setVisibility(View.GONE);
        binding.tvMsg.setText("查询失败");
      }
    });
  }
}


构造方法中我们初始化了两个类 Handler 和 ActivityMainBinding , 其中 handler 用于子线程操作完成后更新主线程 UI , ActivityMainBinding 用于操作 View 控件 .


主要看看 search 方法 . 这个方法在 XML 文件中有被绑定到按钮上 , 没注意的可以上去看看 .


binding.pbLoad.setVisibility(View.VISIBLE);

这个就能实现加载动画的显示了! 很方便吧 不需要 findViewById , 然后就是调用 Model 的方法获取需要的信息 , 这里传入了文本编辑框的内容


searchModel.getIPaddressInfo(binding.etIp.getText().toString().trim(), this);


同样的 binding.etIp.getText() 就能拿到内容了 , 各种方便有木有 .


后面拿到了结果之后就通过接口回调对 View 进行了更新 .


@Override public void onSuccess(final IPAddress ipAddress) {
    handler.post(new Runnable() {
      @Override public void run() {
        binding.pbLoad.setVisibility(View.GONE);
        binding.tvMsg.setText(ipAddress.toString());
      }
    });
  }