侵入式的 GT架构,请参考中篇:
前言:
相信不少伙伴在进行Android开发的时候,肯定遇见过 Activity 代码上千行的,这种代码非常难以维护,牵一发而动全身,像极了某印#国的电线杆的电线一样,网上调侃程序员修水管,越修水越多估计也是这么来的。
而框架意在将这Activity中上千行代码进行功能分类,并提高相同功能的重复使用率,我们大体可将功能简单分为三种 界面代码、业务代码、逻辑代码。让各自专一的完成各自任务。
MVVM模式:
(先解释一下箭头)
View ↔ ViewModel表示:View与ViewModel相互绑定,然后ViewModel与Model又相互持有彼此引用,相互持有引用相信大家已经非常熟悉了,但这相互绑定是怎样的,没了解过的伙伴估计不太理解。
相互绑定的话,View可以通过 ViewModel 从 Model 中获取数据;当获取到了数据之后,会通过自动绑定相互绑定的作用,设置到View界面展示。
相互绑定的作用:可以省去 组件获取、事件注册、数据获取、数据更新这些代码。我们来看看之前MVP中Activity代码,除去Presenter代码与Model代码,View代码里剩下的全是组件获取、事件注册、数据获取、数据更新了,而MVVM就是省去这些代码。
在使用GT库里封装的架构当然需要先依赖好GT库:
详细依赖教程请参看
本次来讲一下怎么使用GT库中封装好的架构来实现搭建简易的MVVM,咋们通过一个例子来讲解
实现效果图:
实现一个简单的添加词条到数据库的功能
咋们先来看看文件结构:
主要目录是 Model View ViewModel 这三个目录
Model目录:写与数据处理有关的
View目录:写与View页面初始化有关的
ViewModel目录:写与业务逻辑有关的
我们最后来看看去掉所有业务逻辑后的 纯 GT_MVVM 架构代码:
GT_MVVM 架构代码
Model 类
/**
* @Description:负责数据(网络请求/数据库)逻辑处理
*/
public class Model<T> extends GT.Frame.GT_Model<T> {
public Model(){
}
}
View 类
@GT_DataBinding(setLayout = "activity_main",setBindingType = GT_DataBinding.Activity)
@GT.Annotations.GT_AnnotationActivity(R.layout.activity_main)
public class MainActivity extends MainActivityBinding<MainViewModel> {
@Override
protected void initView(Bundle savedInstanceState) {
super.initView(savedInstanceState);
GT.logt("持有 ViewModel 引用:" + viewModel);
}
}
不理解自动产生 Binding 类 的同学们可以点击这里去查看自动产生类使用教程
ViewModel 类
/**
* @Description:负责业务逻辑处理
*/
public class MainViewModel extends GT.Frame.GT_BindingViewModel<MainActivity, Model<?>> {
@Override
protected void initView() {
super.initView();
GT.logt("持有 View 引用:" + bindingView);
GT.logt("持有 Model 引用:" + bindingModel);
}
}
这是结合了GT库内部注解后再加载GT库MVVM的代码
使用起来也是非常方便的
如果不使用 GT库自带的注解工具,不继承自动生成的 Binding类 来实现MVVM该怎么实现呢
Model 类
/**
* @Description:负责数据(网络请求/数据库)逻辑处理
*/
public class Model<T> extends GT.Frame.GT_Model<T> {
public Model(){
}
}
View 类
//必要的,用于生成 MainActivityBinding
@GT_DataBinding(setLayout = "activity_main",setBindingType = GT_DataBinding.Activity)
public class MainActivity extends AppCompatActivity {
private MainViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//构建 ViewModel
MainActivityBinding<?> bindingView = GT.DataBindingUtil.setContentView(this);
viewModel = new MainViewModel(bindingView);
GT.logt("持有 ViewModel 引用:" + viewModel);
}
}
不理解自动产生 Binding 类 的同学们可以点击这里去查看自动产生类使用教程
ViewModel 类
/**
* @Description:负责业务逻辑处理
*/
public class MainViewModel extends GT.Frame.MVVM_ViewModel<Model<?>> {
private MainActivityBinding<?> binding;
private Model<?> model;
public MainViewModel(MainActivityBinding<?> binding) {
this.binding = binding;
//构建Model
model = new Model<>();
setBindingView(model);
initView();
}
@Override
protected void initView() {
GT.logt("持有 View 引用:" + binding);
GT.logt("持有 Model 引用:" + model);
}
}
这样架构就搭好了,接下来我们来看看架构搭好了,具体是怎么实用的
我们先看看Model 目录里的:
Model
一个实体类, Hibernate 是GT库里封装好的数据库管理类(与MVVM无关),用于简易生成与使用数据库的
@GT.Hibernate.GT_Bean//标识待扫描入数据库的表
public class DemoBean {
@GT.Hibernate.GT_Key
private int ID;//定义主键
private String time; //时间
private String value; //词条
public int getID() {
return ID;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "DemoBean{" +
"ID=" + ID +
", time='" + time + '\'' +
", value='" + value + '\'' +
'}';
}
}
一个Model 类,用于处理数据逻辑的类
/**
* @Description:负责数据(网络请求/数据库)逻辑处理
*/
public class Model<T> extends GT.Frame.GT_Model<T> {
//数据库管理对象
private GT.Hibernate hibernate;
public void setHibernate(GT.Hibernate hibernate) {
this.hibernate = hibernate;
}
//添加一条数据
public boolean add(String value) {
DemoBean demoBean = new DemoBean();
demoBean.setValue(value.length() == 0 ? "空白" : value);
demoBean.setTime(GT.GT_Date.getDateTime_CH());
return hibernate.save(demoBean)//保存该实体类
.isStatus();//返回本次操作状态
}
//删除一条数据
public boolean delete(int id) {
return hibernate.delete(DemoBean.class, id)//删除单条数据
.isStatus();//返回本次操作状态
}
//清空所有数据
public boolean clearAll() {
return hibernate.deleteAll(DemoBean.class)//删除所有数据
.isStatus();//返回本次操作状态
}
//查询所有数据
public List<DemoBean> queryAll() {
return hibernate.flashback("ID")//按照ID倒序
.queryAll(DemoBean.class);//查询返回
}
}
Model 类这些,可以看出Model类仅仅处理与数据挂钩,接下来我们来看看 View 目录
MainActivity类,主页面
@GT_DataBinding(setLayout = "activity_main", setBindingType = GT_DataBinding.Activity)
@GT.Annotations.GT_AnnotationActivity(R.layout.activity_main)
public class MainActivity extends MainActivityBinding<MainViewModel> {
@GT.Hibernate.Build
private GT.Hibernate hibernate;//构建数据库对象
@Override
protected void initView(Bundle savedInstanceState) {
super.initView(savedInstanceState);
viewModel.setHibernate(hibernate);//将数据库对象给到 ViewModel 处理
}
//注册 单击事件
@GT.Annotations.GT_Click({R.id.btn_add,R.id.btn_clear})
public void onClick(View view) {
viewModel.onClick(view);//将单击事件交给 ViewModel处理
}
}
MainActivity 对应的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/et_data"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:gravity="center"
android:hint="输入词库"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.496"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.05" />
<Button
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="添加"
app:layout_constraintEnd_toStartOf="@+id/btn_clear"
app:layout_constraintStart_toStartOf="@+id/et_data"
app:layout_constraintTop_toBottomOf="@+id/et_data" />
<Button
android:id="@+id/btn_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="清空"
app:layout_constraintEnd_toEndOf="@+id/et_data"
app:layout_constraintStart_toEndOf="@+id/btn_add"
app:layout_constraintTop_toBottomOf="@+id/et_data"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_add" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity继承了GT自动产生的 MainActivityBinding 类, 做了构建数据库并将对象传递给 Model 去管理,不理解自动产生 Binding 类 的同学们可以点击这里去查看自动产生类使用教程
DemoAdapter类,适配器页面
/**
* @Description: 继承使用GT生成的 AdapterBinding ,也可使用其他方式生成的适配器进行搭配
*/
@GT_DataBinding(setLayout = "item_demo", setBindingType = GT_DataBinding.Adapter)
@GT.Annotations.GT_AnnotationAdapter(R.layout.item_demo)
public class DemoAdapter extends DemoAdapterBinding<DemoBean> {
public OnAdapterClick onAdapterClick;
public DemoAdapter(Context context, OnAdapterClick onAdapterClick) {
super(context);
this.onAdapterClick = onAdapterClick;
}
@Override
protected void initView(DemoAdapterViewHolder holder, View itemView, int position, DemoBean demoBean) {
super.initView(holder, itemView, position, demoBean);
holder.tv_1.setText(demoBean.getID() + " 号");
holder.tv_2.setText(demoBean.getValue());
holder.tv_3.setText(demoBean.getTime());
//单击加载
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onAdapterClick.onClick(demoBean);
}
});
//长按操作
itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
onAdapterClick.onLongClick(demoBean);
return true;
}
});
}
//添加一个单击与长按后 传递实体类 给 ViewModel处理 的演示
public interface OnAdapterClick {
void onClick(DemoBean demoBean);
void onLongClick(DemoBean demoBean);
}
}
DemoAdapter 对应的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#07B2FF"
android:gravity="center"
android:layout_margin="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_1"
android:layout_weight="1"
android:gravity="center"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="序列:"
android:textColor="#FFFFFF"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_2"
android:layout_weight="1"
android:gravity="center"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="词语"
android:textColor="#FFFFFF"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_3"
android:layout_weight="1"
android:gravity="center"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="时间"
android:textColor="#FFFFFF"
android:textSize="20sp" />
</LinearLayout>
适配器也使用了 继承使用GT生成的 AdapterBinding ,也可使用其他方式生成的适配器进行搭配
最后我们来看看 ViewModel 怎么进行联合的
/**
* @Description:负责业务逻辑处理
*/
public class MainViewModel
extends GT.Frame.GT_BindingViewModel<MainActivity, Model<?>>
implements DemoAdapter.OnAdapterClick {
private DemoAdapter demoAdapter;//适配器
@Override
protected void initView() {
super.initView();
//初始化并
demoAdapter = new DemoAdapter(bindingView, this);
//设置适配器显示为垂直布局
demoAdapter.setLinearLayoutManager_V(bindingView.rv);
}
//处理单击事件
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_add:
//保存数据,若 保存成功 则 刷新数据
if (bindingModel.add(bindingView.et_data.getText().toString())) {
demoAdapter.setBeanList(bindingModel.queryAll());//刷新数据
GT.toast_s(bindingView, "添加成功");
}
break;
case R.id.btn_clear:
//清空数据,若 清空成功 则 刷新数据
if (bindingModel.clearAll()) {
demoAdapter.setBeanList(bindingModel.queryAll());//刷新数据
GT.toast_s(bindingView, "清空成功");
}
break;
}
}
@Override
public void onClick(DemoBean demoBean) {
GT.logt("来自适配器 item 的单击:" + demoBean);
}
@Override
public void onLongClick(DemoBean demoBean) {
GT.logt("来自适配器 item 的长按:" + demoBean);
//使用GT库自带的 对话框
new GT.GT_Dialog.GT_AlertDialog(bindingView)
.dialogTwoButton(R.drawable.ic_launcher_background,
"长按提示", "是否删除该条数据?",
true, "确定", (dialogInterface, i) -> {
if (bindingModel.delete(demoBean.getID())) {
demoAdapter.setBeanList(bindingModel.queryAll());//刷新数据
GT.toast_s(bindingView, "删除成功");
}
}, "取消", (dialogInterface, i) -> {
}).show();
}
//将数据库对象给到 Model 管理
public void setHibernate(GT.Hibernate hibernate) {
bindingModel.setHibernate(hibernate);//这里赋值后 Model 里的 hibernate 才能工作
demoAdapter.setBeanList(bindingModel.queryAll());//加载数据
}
}
这就是所有的MVVM架构使用的代码了,当然,GT_MVVM目前支持绑定的页面有这些:
GT_DataBinding.Activity;
GT_DataBinding.Fragment;
GT_DataBinding.DialogFragment;
GT_DataBinding.Adapter;
GT_DataBinding.FloatingWindow;
GT_DataBinding.PopupWindow;
GT_DataBinding.View;
下面这是本次教程的Demo源码,里面有上面各种页面绑定MVVM的具体使用案例:
本章源码地址