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