MVVM框架主要的特点就是低耦合,对于不同的view,model可以复用。
目前android 的MVVM框架主要是使用 databinding实现双向数据绑定,来降低耦合度。
首先是用databinding来实现mvvm框架,下面是一个我自己总结的使用databinding来实现mvvm框架的简单示例。
之后是我使用Viewbinding实现的框架,可以说用起来更加的灵活便捷。
model类,这个类主要是一些与视图相关的业务逻辑的处理,对于一些需要复用并且实现不同逻辑的model类,可以使用回调的方式来执行不同的业务逻辑。例如下面代码中的titlemodel, 需要更新数据的时候,需要调用notifyPropertyChanged(BR.title)来刷新页面数据
public abstract class BaseModel<T> extends BaseObservable {
protected ProgressDialog mProgressDialog;
protected T binding;
protected Activity context;
public BaseModel(T binding, Activity context){
this.binding=binding;
this.context=context;
}
public BaseModel(T binding){
this.binding=binding;
}
}
public class TitleModel extends BaseModel<LayoutTextTitleBinding> {
private String title;
private OnClick onClick;
private int actionIcon;
public void setOnClick(OnClick onClick) {
this.onClick = onClick;
}
public TitleModel(LayoutTextTitleBinding binding) {
super(binding);
}
@Bindable
public int getActionIcon() {
return actionIcon;
}
public void setActionIcon(int actionIcon) {
this.actionIcon = actionIcon;
// notifyPropertyChanged(BR.titleModel);
}
public void onBack(View view) {
onClick.onBack(view);
}
public void onAction(View view) {
onClick.onAction(view);
}
@Bindable
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
// notifyPropertyChanged(BR.titleModel);
}
public static interface OnClick{
void onBack(View view);
void onAction(View view);
}
}
public class TestActivityModel extends BaseModel<LayoutTestActivityBinding> {
private TestAdapter testAdapter;
public TitleModel titleModel;
public TestActivityModel(LayoutTestActivityBinding binding) {
super(binding);
}
public TestActivityModel(LayoutTestActivityBinding binding, final Activity activity) {
super(binding, activity);
titleModel=new TitleModel(binding.title);
titleModel.setActionIcon(R.mipmap.ic_launcher);
titleModel.setOnClick(new TitleModel.OnClick() {
@Override
public void onBack(View view) {
Toast.makeText(activity,"按下返回键",Toast.LENGTH_SHORT).show();
}
@Override
public void onAction(View view) {
Toast.makeText(activity,"按下动作键",Toast.LENGTH_SHORT).show();
}
});
binding.title.setTitleModel(titleModel);
LinearLayoutManager layoutManager =new LinearLayoutManager(activity,LinearLayoutManager.VERTICAL,false);
testAdapter = new TestAdapter(activity);
testAdapter.setOnClick(new TestAdapter.OnClick() {
@Override
public void onClick(View view, TestEntity info, int position) {
}
@Override
public void onLongClick(View view, TestEntity info, int pos) {
}
});
binding.list.setLayoutManager(layoutManager);
binding.list.setAdapter(testAdapter);
getData();
}
int count;
private void getData(){
ArrayList<TestEntity> data=new ArrayList<>();
for(int i=count;i<count+10;i++){
TestEntity entity=new TestEntity();
entity.setContent("context"+i);
entity.setName("item"+i);
data.add(entity);
}
count+=10;
testAdapter.setmDatas(data);
testAdapter.notifyDataSetChanged();
}
public void onRefresh(View view){
getData();
}
}
关于Viewbinding。viewbinding会比databinding更加简单。并且不需要在xml文件中进行修改。下面是一个关于viewbinding的示例。同样是mvvm框架,使用viewbinding改进以后可以更加灵活的使用。比如两个完全不同的布局,也可以使用同一个model。不仅仅是相同视图情况下代码重用。下面是两种model的写法,一种指定了viewbinding的具体类型,一种没有。对于需要复用的model类,如果布局不复用也可以不指定具体的model类型,可以通过代码回调或者类型判断来执行不同的业务逻辑处理。需要复用的model不和activity关联,所以提供下面SimpleBaseModel的形式。
public abstract class BaseModel<T extends ViewBinding> implements View.OnClickListener {
protected T binding;
protected Activity context;
public BaseModel(T binding,Activity context){
this.binding=binding;
this.context=context;
}
public BaseModel(T binding){
this.binding=binding;
}
public abstract void onResume();
public abstract void onActivityResult(int requestCode, int resultCode, Intent data);
public abstract void onPause();
public abstract void onDestroy();
public abstract void onRestart();
public abstract void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults);
protected void bindListener(View... views){
for (View view:
views) {
view.setOnClickListener(this);
}
}
}
public abstract class SimpleBaseModel <T extends ViewBinding> implements View.OnClickListener {
protected T binding;
protected Activity context;
public SimpleBaseModel(T binding, Activity context){
this.binding=binding;
this.context=context;
}
public SimpleBaseModel(T binding){
this.binding=binding;
}
protected void bindListener(View... views){
for (View view:
views) {
view.setOnClickListener(this);
}
}
}
public abstract class BaseActivity<T extends ViewBinding,M extends BaseModel> extends AppCompatActivity{
protected T binding;
protected M model;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onPrepare();
binding=getBinding();
setContentView(binding.getRoot());
model=getModel();
}
@Override
protected void onDestroy() {
super.onDestroy();
model.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
model.onPause();
}
@Override
protected void onResume() {
super.onResume();
model.onResume();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
model.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onRestart() {
super.onRestart();
model.onRestart();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
model.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected abstract void onPrepare();
protected abstract T getBinding();
protected abstract M getModel();
}
下面是使用的例子,viewbinding可以灵活的引用include导入的布局,这里只是做一个范例,如果只是修改标题这种简单的逻辑,可以直接使用第三种写法。 其他两种model的形式,可以用于业务逻辑比较多的代码逻辑。对于model1的写法,是有重复布局的情况。model2的写法,是应用于业务逻辑大量重复,但布局不同的情况。可以在代码判断传入的binding的类型,对不同的视图进行不同的处理,也可以增加公共的回调接口进行处理
public class MainModel extends BaseModel<ActivityMainBinding> {
TitleModel titleModel;
TitleModel2 titleModel2;
public MainModel(ActivityMainBinding binding, Activity context) {
super(binding, context);
titleModel=new TitleModel(binding.title,context);
titleModel.setTitle("标题");
titleModel2=new TitleModel2(binding.title,context);
titleModel2.setTitle();
binding.title.tx.setText("标题");//第三种写法
}
public MainModel(ActivityMainBinding binding) {
super(binding);
}
@Override
public void onResume() {
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
@Override
public void onPause() {
}
@Override
public void onDestroy() {
}
@Override
public void onRestart() {
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
}
@Override
public void onClick(View v) {
}
}
public class TitleModel extends SimpleBaseModel<LayoutTitleBinding>{
public TitleModel(LayoutTitleBinding binding, Activity context) {
super(binding, context);
}
public void setTitle(String str){
binding.tx.setText(str);
}
private void func(){
//需要重复的业务逻辑
}
@Override
public void onClick(View v) {
}
}
public class TitleModel2 extends SimpleBaseModel{
public TitleModel2(ViewBinding binding, Activity context) {
super(binding, context);
}
public void setTitle(){
if(binding instanceof LayoutTitleBinding) {
((LayoutTitleBinding) binding).tx.setText("test");
}
}
private void func(){
//需要重复的业务逻辑
}
@Override
public void onClick(View v) {
}
}
public class MainActivity extends BaseActivity<ActivityMainBinding,MainModel> {
@Override
protected void onPrepare() {
}
@Override
protected ActivityMainBinding getBinding() {
return ActivityMainBinding.inflate(getLayoutInflater());
}
@Override
protected MainModel getModel() {
return new MainModel(binding,this);
}
}
最后贴出adapter和fragment的写法和用法,利用泛型可以节省大量的代码
public abstract class BaseAdapter<T, B extends ViewBinding> extends RecyclerView.Adapter<BaseAdapter.ViewHolder> {
protected OnItemClick onItemClick;
protected List<T> mDatas;
protected Context context;
public List<T> getmDatas() {
return mDatas;
}
public void setmDatas(List<T> mDatas) {
this.mDatas = mDatas;
}
public BaseAdapter(Context context) {
this.context = context;
}
public BaseAdapter(Context context, List<T> mDatas) {
this.mDatas = mDatas;
this.context = context;
}
public OnItemClick getOnItemClick() {
return onItemClick;
}
public void setOnItemClick(OnItemClick onItemClick) {
this.onItemClick = onItemClick;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
B binding = setBinding(inflater, parent);
ViewHolder<B> viewHolder = new ViewHolder<B>(binding.getRoot());
viewHolder.setBinding(binding);
if(onItemClick!=null){
viewHolder.setOnItemClick(onItemClick);
}
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ViewHolder<B> myholder=(ViewHolder<B>) holder;
onBindHolder(myholder,position);
}
protected abstract void onBindHolder(ViewHolder<B> holder, int position);
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
protected abstract B setBinding(LayoutInflater inflater, ViewGroup parent);
@Override
public int getItemCount() {
return mDatas == null ? 0 : mDatas.size();
}
public static class ViewHolder<B extends ViewBinding> extends RecyclerView.ViewHolder implements View.OnClickListener {
protected OnItemClick onItemClick;
public B binding;
public OnItemClick getOnItemClick() {
return onItemClick;
}
public void setOnItemClick(OnItemClick onItemClick) {
this.onItemClick = onItemClick;
}
public B getBinding() {
return binding;
}
public void setBinding(B binding) {
this.binding = binding;
}
public ViewHolder(View itemView) {
super(itemView);
}
public void setClick(View... views){
if(onItemClick!=null) {
for (View view : views) {
view.setOnClickListener(this);
}
}
}
@Override
public void onClick(View v) {
onItemClick.onClick(v,getAdapterPosition());
}
}
public static interface OnItemClick{
void onClick(View view, int position);
}
}
public class TestAdapter extends BaseAdapter<String,ItemTestBinding> {
public TestAdapter(Context context, List <String>mDatas) {
super(context, mDatas);
}
@Override
protected void onBindHolder(ViewHolder<ItemTestBinding> holder, int position) {
holder.getBinding().tx.setText(mDatas.get(position));
}
@Override
protected ItemTestBinding setBinding(LayoutInflater inflater, ViewGroup parent) {
return ItemTestBinding.inflate(inflater,parent,false);
}
}
public abstract class BaseFragment<T extends ViewBinding,M extends BaseModel> extends Fragment {
protected T binding;
protected M model;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
onPrepare();
binding=getBinding(inflater,container,savedInstanceState);
model=getModel();
return binding.getRoot();
}
protected abstract void onPrepare();
protected abstract T getBinding(LayoutInflater inflater, @Nullable ViewGroup container,@Nullable Bundle savedInstanceState);
protected abstract M getModel();
}
源码已经上传github