ViewBinding與MVVM框架

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章