一:序
从一开始,小菜鸟我从MVC到用轮子,再到学习MVP,最终发现网上好多人都说使用MVVM的感觉都不一样,
所以到最后我就试着学习一个MVVM,发现代码的整洁度确实不同凡响。
在此记录如何使用以及自己的一些感悟。
二:MVVM
基本介绍
Android 官方把他们设计的架构模式封装成了一系列类的集合,其核心思想为MVVM,基于此扩展成为Android系统量身定做的架构模式,名为: Architecture Components(架构组件)。
有什么优点
减少样板代码,消除代码冗余(比MVP代码体积小很多)
针对Android系统的特点,设计出更实用功能
更高级的业务分离方式
有什么缺点
数据绑定使得 Bug 很难被调试。
你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存
。
数据双向绑定不利于代码重用
。客户端开发最常用的重用是View,但是数据双向绑定技术,让你在一个View都绑定了一个model,不同模块的model都不同。那就不能简单重用View了。
主要分为四个组件类,今天先进行学习第一个DataBinding.
DataBinding
Lifecycle
LiveData
ViewModel
三:DataBinding
由上面的分析来讲,看到前端的同学肯定对双向绑定
并不陌生,这在vue里面是一个十分重要的功能,用起来也十分的方便。而DataBinding也差不多实现了这样的一个功能,并且在代码中也不用长篇大论的findViewById
了.
那就让我们直接近距离感受一下把。
在build.gradle
中开启功能
android {
…
dataBinding {
enabled = true
}
}
在gradle.properties
启动新的编译器
android.databinding.enableV2=true
(1)创建数据源Bean
public class User {
public final String firstName;
public final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void onSaveClick(View view){
System.out.println("绑定监听器。");
}
}
(二)在xml中,增加< layout >
和< data >
标签声明.
要注意,所有的DataBinding的xml,最外层都必须用layout包裹,否则会出错!.
<?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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="user" type="com.example.databinding.User"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_weight="1"
android:text="@{user.firstName}"
/>
</RelativeLayout>
</layout>
(三)在activity中使用。在当你创建了一个databinding类的布局文件后,会自动生成一个相对应的绑定类,命名规则为:驼峰化xml文件名 + Binding.java
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
有了bindding对象后,就说明你就有了布局文件了,可以直接使用里面的控件
binding.name.setBackgroundColor(Color.WHITE);
当然,我们不需要手动为每个控件赋值,只需要对bingding进行对象的设置就可以了。
User user = new User("Test", "User");
binding.setUser(user);
实现效果
四:在RecycleView中使用DatraBinding
创建一个BaseBean.
public interface BaseMessage {
final int TextMessage = 0;
final int ImageMessage = 1;
final int ButtonMessage =2;
int getType();
}
创建两个Bean继承其之
TextMessageBean
public class TextMessageBean extends BaseObservable implements BaseMessage{
String msgTime = "";
String content = "";
public ButtonMessageBean(String message){
setContent(message);
}
}
ButtonMessageBean
public class ButtonMessageBean extends BaseObservable implements BaseMessage{
String msgTime = "";
String content = "";
public ButtonMessageBean(String message){
setContent(message);
}
}
创建Adapter,继承RecyclerView.Adapter<RecyclerView.ViewHolder>
//这个是返回创造ViewHolder
//一个适配器,ViewHolder里面返回的一个ViewDataBinding
//onCreateViewHolder可以根据传入不同的类型,去形成不同的ViewDataBinding-->有不同的item布局了。
//onBindViewHolder时需要根据不同的类型去设置不同的值即可。
public class jvvm_recycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<BaseMessage> list = null;
private Context context = null;
private int gviewType;
public jvvm_recycleAdapter(Context cont, List<BaseMessage> li) {
this.list = li;
this.context = cont;
}
@NonNull
@Override
//根据传入的不同List显示不同的DataBinding
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ViewDataBinding binding = null;
System.out.println("啥玩意:"+viewType);
gviewType = viewType;
switch (viewType){
case BaseMessage.TextMessage:
binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.recycle_image_item,parent,false);
break;
case BaseMessage.ButtonMessage:
binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.recycler_button_item,parent,false);
break;
}
return new MyViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
switch (gviewType){
case BaseMessage.TextMessage:
((MyViewHolder)holder).getBinding().setVariable(BR.TextMessageBean,list.get(position));
break;
case BaseMessage.ButtonMessage:
System.out.println("测试一下");
((MyViewHolder)holder).getBinding().setVariable(BR.ButtonMessageBean,list.get(position));
break;
}
((MyViewHolder)holder).getBinding().executePendingBindings();
}
@Override
public int getItemCount() {
return list.size();
}
@Override
public int getItemViewType(int position) {
return list.get(position).getType();
}
class MyViewHolder extends RecyclerView.ViewHolder{
ViewDataBinding binding = null;
public MyViewHolder(ViewDataBinding itemView) {
super(itemView.getRoot());
this.binding = itemView;
}
public ViewDataBinding getBinding(){
return binding;
}
}
}
创建其总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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="user" type="com.example.databinding.User"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_weight="1"
android:text="@{user.firstName}"
/>
<androidx.recyclerview.widget.RecyclerView
android:layout_below="@id/text"
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_weight="1">
</androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>
</layout>
其中的子item布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="TextMessageBean" type="com.example.databinding.TextMessageBean"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{TextMessageBean.content}"
/>
</RelativeLayout>
</layout>
子item布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="ButtonMessageBean" type="com.example.databinding.ButtonMessageBean"/>
</data>
<RelativeLayout
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:layout_weight="1"
android:text="@{ButtonMessageBean.content}"
android:textColor="@color/red"
/>
</RelativeLayout>
</layout>
Activitry中使用
public class jvvm extends AppCompatActivity {
private static jvvm_recycleAdapter adapter;
private static RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityJvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_jvvm);
User user = new User("Test", "User");
binding.setUser(user);
//通过bindding获得test
recyclerView = binding.recyclerview;
adapter = new jvvm_recycleAdapter(this, initData());
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
public List<BaseMessage> initData() {
List<BaseMessage> list = new ArrayList<>();
list.add(new TextMessageBean("新消息一"));
list.add(new TextMessageBean("新消息二"));
list.add(new TextMessageBean("新消息三"));
list.add(new TextMessageBean("新消息四"));
list.add(new TextMessageBean("新消息五"));
list.add(new TextMessageBean("新消息六"));
list.add(new ButtonMessageBean("ppppppppppp"));
list.add(new ButtonMessageBean("xxxxxxxxxxx"));
return list;
}
}
代码全部给出,重点在于适配器Adapter,可以看到,在初始化根据传入的不同的Bean,在OnCreateViewHolder方法内,会根据不同的type,去创建返回带不同布局的binding的Holder.不同的type是根据getItemViewType方法获得的
在onBindViewHolder中,也是根据不同的type,根据((MyViewHolder)holder).getBinding().setVariable(x,y)
去设置了不同的值。
这样实现了一个Recycleview的不同item的效果。
效果图如下.
五:总结
可能,各位还没有意识到其中发生的一些神奇的事情,使用DataBinding最让人舒服的点在于,去设置控件大小,而且设置点击事件的话,直接在Modle层就可以设置,只需要在xml中进行一个数据源的绑定即可。这样自然就省了很多代码量以及想如何设置的问题。
另外绑定监听设备,也是可以直接写到Model中去的,所以配置我们从拿到对象一步一步对控件的设置,就转变到了,如何设置数据源的问题上了~
当然,你说findByid可以用ButterKnife代替,但是这两个是不同的概念喔~