MVVM 是什么?
MVVM 是 (Model-View-ViewModel)的缩写
MVVM 也是MVC 的架构的一个改进版,
在android中随着项目越来越大,fragment 和activity 变得越来越大,控制器层代码变得臃肿。因此将控制层中的部分代码抽到布局文件里。从而减少控制层臃肿的代码,并且让开发人员很容易的看出那些是动态界面。抽出的动态代码放入ViewModel还方便了开发测试和验证
MVVM的优点
- 低耦合 视图(view) 独立于Model 的变化 和修改,并且一个view 可以绑定不同的viewModel
- 可重用性 把视图逻辑放在一个viewmodel里面
- 独立开发 因为分层每一层的人员不需要了解另外一层的具体实现,所以更容易开发
- 可测试性 直接对viewmodle进行测试
MVVM 在android 中的dataBinding 的实现方式
目标: 实现一个播放音蘋的软件
一个主activity托管fragment
一个实际展示界面的fragment
首先在build.gradle(app)文件中加上
dataBinding {
enabled = true
}
然修改 layout 中 fragment_beat_box 的 xml文件
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="10"
/>
</LinearLayout>
</layout>
BeatBoxFragment java 代码
public class BeatBoxFragment extends Fragment {
private BeatBox mBeatBox;
public static BeatBoxFragment newInstance(){
return new BeatBoxFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mBeatBox = new BeatBox(getActivity());
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
FragmentBeatBoxBinding binding = DataBindingUtil
.inflate(inflater,R.layout.fragment_beat_box,container,false);
binding.recycleView.setLayoutManager(new GridLayoutManager(getActivity(),3));
binding.recycleView.setAdapter(new SoundAdapter(mBeatBox.getSounds(),binding));
return binding.getRoot();
}
@Override
public void onDestroy() {
super.onDestroy();
mBeatBox.relase();
}
}
关键的代码就是
FragmentBeatBoxBinding binding = DataBindingUtil
.inflate(inflater,R.layout.fragment_beat_box,container,false);
binding.recycleView.setLayoutManager(new GridLayoutManager(getActivity(),3));
binding.recycleView.setAdapter(new SoundAdapter(mBeatBox.getSounds(),binding));
return binding.getRoot();
DataBindingUtil.inflate(inflater,R.layout.fragment_beat_box,container,false);// 用于生成与xml对应的绑定对象
FragmentBeatBoxBindg
类是系统自动生成的类 它与 fragment_beat_box.xml 文件绑定在一起
那么这个系统生成的类有什么用呢?,他可以直接调取那些有id的控件
例如fragment_beat_box.xml
中 recycleView的 id 就是 reycycleView 所以
直接调用 FragmentBeatBoxBinding. reycycleView
就行,相当于你直接省去了声明各种控件变量 和findviewbyid 操作的各种麻烦
binding.getRoot()
返回的是这个绑定的xml文件生成的view对象
然后我们再来看Recyclview 中的ViewHolder
private class SoundHolder extends RecyclerView.ViewHolder{
private ListItemSoundBinding mBinding;
private SoundHolder(ListItemSoundBinding binding){
super(binding.getRoot());
mBinding = binding;
mBinding.setViewModel(new SoundViewModel(mBeatBox));
}
public void bind(Sound sound){
mBinding.getViewModel().setSound(sound);
mBinding.executePendingBindings();
}
}
private class SoundAdapter extends RecyclerView.Adapter<SoundHolder>{
private List<Sound> mSounds;
public SoundAdapter(List<Sound> sounds,FragmentBeatBoxBinding mFragmentBeatBoxBinding) {
mSounds = sounds;
}
@NonNull
@Override
public SoundHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(getActivity());
ListItemSoundBinding binding =DataBindingUtil.inflate(inflater,R.layout.list_item_sound,viewGroup,false);
return new SoundHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull SoundHolder soundHolder, int i) {
Sound sound = mSounds.get(i);
soundHolder.bind(sound);
}
@Override
public int getItemCount() {
return mSounds.size();
}
}
我们看关键代码
mBinding.setViewModel(new SoundViewModel(mBeatBox));
其中mBinding
对象是 和上面一样由DataBindingUtil
类由对应的Lis_tItem_SoundBinding.xml
文件生成的ListItemSoundBinding
对象
这个对象可以绑定一个由你编写的ViewModel 对象
绑定后可以直接在Lis_tItem_SoundBinding.xml
文件中调用
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.chl.beatbox.viewmodel.SoundViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:text="@{viewModel.title}"
tools:text="Sound name"
android:onClick="@{()-> viewModel.onButtonClicked()}" />
</FrameLayout>
</layout>
然后在xml中data标签中 variable name 对应的是变量名,然后可以直接调用viewmodel的方法
ViewModel代码
package com.example.chl.beatbox.viewmodel;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import com.example.chl.beatbox.model.Sound;
import com.example.chl.beatbox.resourceManger.BeatBox;
public class SoundViewModel extends BaseObservable {
private Sound mSound;
private BeatBox mBeatBox;
public SoundViewModel(BeatBox beatBox) {
mBeatBox = beatBox;
}
public Sound getSound() {
return mSound;
}
@Bindable
public String getTitle(){
return mSound.getName();
}
public void setSound(Sound sound) {
mSound = sound;
notifyChange();
}
public void onButtonClicked(){
mBeatBox.play(mSound);
}
}
关键代码
@Bindable
public String getTitle(){
return mSound.getName();
}
public void setSound(Sound sound) {
mSound = sound;
notifyChange();
}
@bindable
可以直接实时通知view更新
notifychange()
通知刷新所有绑定的数据
主要的代码就是如此