Android MVVM
1.MVVM概述
Model-View-ViewModel模式。将View的状态和行为完全抽象化,把逻辑与界面的控制交给ViewModel进行处理。
1.1 三部分组成:
- View
- 进行视图控件的初始化设置,不具有任何的数据逻辑处理。
- Model
- 定义实体类以及获取业务数据模型
- ViewModel
- 连接View和Model的桥梁,ViewModel与Model进行交互,处理完业务逻辑后,通过DataBinding将数据变化反应到View上。
1.2 优点:
- 低耦合度,当UI发生变化时,ViewModel不需要太多的改动。
- 数据驱动,数据的变化会引发UI的变化,UI的改变也会使得数据Model进行对应的更新。
- 异步线程更新Model,数据绑定框架会将异步线程中的数据变化通知到UI线程中交给View去更新。
- 方便协作,View层和数据处理完全解耦合,可以并发进行。
- 易于单元测试,ViewModel只负责处理数据,View层只负责输入指定格式的数据即可进行测试。
- 数据复用,ViewModel层对数据的获取和处理逻辑,其实是Repository模式时,获取的数据是可复用的。
2.DataBinding
MVVM是一种思想,一种架构模式。
MVVM 相对于 MVP,其实就是将 Presenter 层替换成了 ViewModel 层。
DataBinding是Google推出的实现MVVM的工具。 是实现视图和数据双向绑定的工具,用声明式布局文件来减少粘结业务逻辑和布局文件的胶水代码。
DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常
2.1 配置方式
android {
dataBinding {
enabled = true
}
}
2.2 使用方式
1.布局文件的根标签是layout标签,内部包含一个data元素和一个view元素。view为没有使用DataBinding时候的布局文件。
<layout xmlns=android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.mvvmdemo.UserBean"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.sex}"/>
</LinearLayout>
</layout>
2.data中的user就是定义的user实体类,当向DataBinding中设置好user类后,textView会自动设置text的取值。
public class UserBean{
public ObservableField<String> name = new ObservableField<>();//当实体类中的值发生变化时,会自动通知View刷新
public ObservableField<String> sex = new ObservableField<>();
public UserBean(){
name.set("王小明");
sex.set("男");
}
}
3.在Activity中绑定layout
ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserBean user = new UserBean();
activityMainBinding.setUser(user);
2.3 核心优势
解决了将数据分解映射到各个view的问题。
- 在编译阶段,会生成一个ViewDataBinding对象,该对象持有Activity要展示的数据和布局中的view的引用。
- 数据分解到各个view,在UI线程上更新数据、监听数据变化,实时更新,实现了数据和布局的紧紧绑定在一起。
2.4 实现数据变化自动驱动UI刷新的方式_单向绑定
有三种
- BaseObservable
- ObservableField
- ObservableCollection
2.4.1 BaseObservable
提供了notifyChange()和notifyPropertyChanged(),前者刷新所有值,后者只更新对应的BR的flag,该BR的生成通过@Bindable生成。
public class Goods extends BaseObservable{
@Bindable
public String name;//如果是public修饰符,则在成员变量上添加@Bindable注解
private String details;//private变量,则在Get方法是@Bindable注解
@Bindable
public String getDetails(){
return details;
}
public void setName(String name){
this.name = name;
notifyPropertyChanged(com.demo.databindingsamples.BR.name);//仅更新name取值
}
public void setDetails(String details){
this.details = details;
notifyChange();//更新所有值
}
}
实现BaseObservable接口的类,允许注册一个监听器。观察对象属性更改时会通知这个监听器。onPropertyChanged()
2.4.2 ObservableField
public class ObservableGoods{
private ObservableField<String> name;
}
对ObservableGoods属性值的改变,都会立刻触发UI刷新。
2.4.3 ObservableCollection
DataBinding提供了包装类用于替代原生的List和Map,分别是ObservableList和ObservableMap
使用方式
<data>
<variable name="list"
type="ObservableList<String">>"/>
<variable name="map"
type="ObservableMap<String,Strinng">>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>
<TextView
android:text="@{list[index]}"
/>
<TextView
android:text="@{map[key]}"
private ObservaleMap<String, String> map;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
ActivityMain5Binding binding = DataBindingUtil.setContentView(this, R.layout.activity_new);
map = new ObservableMap<>();
map.put("name","zhangsan");
map.put("age","23");
binding.setMap(map);
ObservableList<String> list = new ObservableArrayList<>();
list.add("ye");
list.add("ee");
binding.setList(list);
binding.setIndex(0);
binding.setKey("name");
}
2.5 双向绑定
当数据改变时,视图会刷新。视图改变时,也会改变数据。
<TextView android:text="@={goods.name}"
2.6 事件绑定
可以用于以下回调事件
- android:onClick
- android:onLongClick
- android:afterTextChanged
- android:onTextChanged
- ……
<TextView
android:onClick="@{)->userPresenter.onUserNameClick(userInfo)}"/>
<EditText
android:onClick="@{userPresenter.afterTextChanegd}"/>
3.MVVM实例
3.1 项目目录
demo
model
Feed
News
view
MainActivity
viewmodel
MainViewModel
3.2 View的实现
public class MainActivity extends AppCompatActivity {
public ActivityMainBinding mActivityMainBinding;
private MainViewModel mViewModel;
public NewsAdapter mNewsAdapter;
public List<News> mNewsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//设置dataBinding、viewModel
mActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mViewModel = new MainViewModel();
mActivityMainBinding.setViewModel(mViewModel);
//初始化RecycleView
LinearLayoutManager layoutManager = new LinearLayoutManager();
mActivityMainBinding.recycleView.setLayoutManager(layoutManager);
mNewsAdapter = new NewsAdapter(this, mNewsList);
mActivityMainBinding.recycleView.setAdapter(mNewsAdapter);
//加载数据
mViewModel.loadNews();
}
}
3.3 ViewModel
public class MainViewModel {
private MainActivity mActivity;
public MainViewModel(MainActivity activity){
mActivity = activity;
}
public void loadNews(){
String feedUrl = "";
Feed feed = new Feed();
feed.loadData(feedUrl, new LoadListener<News>(){
@Override
public void loadSucess(List<News> list){
//数据加载成功
mActivity.mNewsList.addAll(list);//数据更新
mActivity.mNewAdapter.notifyDataSetChanged();
}
@Override
public void loadFailure(String message){
//数据加载失败
}
});
}
}
public interface INews{
@GET(".")
Call<Feed> getFeed();
}
3.4 Model
public class Feed {
private List<News> data;
public void loadData() {
...
INews inews = retrofit.create(INews.class);//???
Call<Feed> feed = iNews.getFeed();
feed.enqueue(new Calback<Feed>() {
@Override
public void onResponse(Call<Feed> call, Response<Feed> response){
//数据获取成功
List<News> newsList = new ArrayList<>();
...
loadListener.loadSuccess(newsList);
}
});
data
}
}
4. 参考文档