ViewBinding与MVVM框架

  • Post author:
  • Post category:其他


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

地址:

https://github.com/roofroot/test_view_binding



版权声明:本文为u011334510原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。