完整的MVVM设计模式的例子(使用RecyclerView)

    MVVM设计模式,在此处就不介绍,网上有很多大牛都有很详细的讲解。

    相信大家看了MVVM设计模式后,不太清楚到底应该怎么写。本人不才,现将个人写的一个小Demo分享给大家,请大家批评指针。

如图所示:

项目的整体结构:

运行的效果:

第一部分:写一写BaseX相关的东西

    因为使用MVVM,V中负责显示,ViewModel需要持有一个View的实例(不是binding).

  •     IBaseView(显示的一些公共的部分)
     
public interface IBaseView {
    // 显示错误提示
    void showError(String message);

    //显示成功提示
    void showSuccess(String message);

    //显示正在加载
    void showLoading();

    void complete();
}
  • IBaseVM(ViewModel的Base)
    因为ViewModel需要持有一个IBaseView的实例,(当从Model中获取数据后,或者显示错误/显示数据获取完成等需要使用接口的方式反馈给View)
public interface IBaseVM<V extends IBaseView> {

    void attachView(V view);

    void detachView();

    V getIView();
}
  • BaseVM (通过attachView和detachView的方式,获取BaseView的引用和解除)
public abstract class BaseVM<V extends IBaseView> implements IBaseVM{
    private WeakReference<IBaseView> mVWeakReference;
    @Override
    public void attachView(IBaseView view) {
        mVWeakReference=new WeakReference<>(view);
    }

    @Override
    public void detachView() {
        if (mVWeakReference != null) {
            mVWeakReference.clear();
            mVWeakReference=null;
        }
    }

    @Override
    public IBaseView getIView() {
        if (mVWeakReference != null) {
           return mVWeakReference.get();
        }
        return null;
    }
}
  • BaseBindingAdapter

    在使用recyclerview中,难免会写适配器adapter,此处将上拉加载,下拉刷新进行封装。

public abstract class BaseBindingAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
    public List<T> mDataList;
    public Context mContext;

    public BaseBindingAdapter(Context context) {
        mContext = context;
        mDataList = new ArrayList<>();
    }

    @Override
    public int getItemCount() {
        return mDataList.size();
    }

    @Override
    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
        return onCreateVH(parent, viewType);
    }

    @Override
    public void onBindViewHolder(VH holder, int position) {
        onBindVH(holder, position);
    }

    /**
     * Author: MrGao
     * CreateTime: 2018/4/6 17:12
     * MethodName:
     * Des:刷新数据
     * Params:
     * Return:
     **/
    public void onRefreshData(List<T> list) {
        if (mDataList != null) {
            mDataList.clear();
            mDataList.addAll(list);
            notifyDataSetChanged();
        }
    }

    public void onLoadMoreData(List<T> list) {
        if (mDataList != null && list != null) {
            mDataList.addAll(list);
            notifyDataSetChanged();
        }
    }


   public abstract VH onCreateVH(ViewGroup parent, int viewType);

   public abstract void onBindVH(VH holder, int position);
}
  • BaseBindViewHolder

   在写recyclerview的viewholder的时候,和以前不一样了,应该使用binding的方式,在adapter中通过

DataBindingUtil.inflate()的方式获取到binding对象。

public class BaseBindViewHolder extends RecyclerView.ViewHolder {
    ViewDataBinding mDataBinding;
    public BaseBindViewHolder(ViewDataBinding binding) {
        super(binding.getRoot());
        mDataBinding=binding;
    }

    public ViewDataBinding getBinding() {
        return mDataBinding;
    }
}
 

第二部分:MVVM的View

    MVVM的View,在activity中用于显示数据的作用   ,采用了DataBindingUtil加载布局并获取Binding对象。此处的下拉刷新和上拉加载使用了本人自己封装的LucklyRecyclerView库。(欢迎大家star)。

public class MainActivity extends AppCompatActivity implements MainBaseView, LucklyRecyclerView.OnLoadMoreListener, LucklyRecyclerView.OnRefreshListener {
    private ActivityMainBinding mMainBinding;
    private MainDataAdapter mDataAdapter;
    private MainViewModelImpl mMainViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        initView();


    }

    /**
     * 初始化布局
     */
    private void initView() {
        mDataAdapter = new MainDataAdapter(this);
        mMainBinding.recylerView.setLayoutManager(new LinearLayoutManager(this));
        mMainBinding.recylerView.setLoadMoreListener(this);
        mMainBinding.recylerView.setOnRefreshListener(this);
        mMainBinding.recylerView.setAdapter(mDataAdapter);

        if (mMainViewModel == null) {
            mMainViewModel = new MainViewModelImpl();
        }
        mMainViewModel.attachView(this);//将IBaseView对象传递给ViewModel,这样ViewModel才能使用接口的方式反馈数据给View
        mMainViewModel.setDataAdapter(mDataAdapter);

        mMainViewModel.refresh();

    }

    /**
     * 显示错误信息
     *
     * @param message
     */
    @Override
    public void showError(String message) {

    }

    /**
     * Author: MrGao
     * CreateTime: 2018/4/6 16:22
     * MethodName:
     * Des:显示成功信息
     * Params:
     * Return:
     **/
    @Override
    public void showSuccess(String message) {

    }

    /**
     * Author: MrGao
     * CreateTime: 2018/4/6 16:22
     * MethodName: 显示正在加载等信息
     * Des:
     * Params:
     * Return:
     **/
    @Override
    public void showLoading() {

    }

    @Override
    public void onLoadMore() {
        mMainBinding.recylerView.setLoading();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mMainViewModel.loadMore();


            }
        }, 1200);
    }

    @Override
    public void complete() {
        mMainBinding.recylerView.setLoadingComplete();
        mMainBinding.recylerView.setRefreshComplete();
    }

    @Override
    public void onRefresh() {
        mMainBinding.recylerView.setRefreshEnable(true);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mMainViewModel.refresh();

            }
        }, 1200);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mMainViewModel != null) {
            mMainViewModel.detachView();
        }
    }
}
        if (mMainViewModel == null) {
            mMainViewModel = new MainViewModelImpl();
        }
        mMainViewModel.attachView(this);//将IBaseView对象传递给ViewModel,这样ViewModel才能使用接口的方式反馈数据给View
        mMainViewModel.setDataAdapter(mDataAdapter);

        mMainViewModel.refresh();

    }

    /**
     * 显示错误信息
     *
     * @param message
     */
    @Override
    public void showError(String message) {

    }

    /**
     * Author: MrGao
     * CreateTime: 2018/4/6 16:22
     * MethodName:
     * Des:显示成功信息
     * Params:
     * Return:
     **/
    @Override
    public void showSuccess(String message) {

    }

    /**
     * Author: MrGao
     * CreateTime: 2018/4/6 16:22
     * MethodName: 显示正在加载等信息
     * Des:
     * Params:
     * Return:
     **/
    @Override
    public void showLoading() {

    }

    @Override
    public void onLoadMore() {
        mMainBinding.recylerView.setLoading();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mMainViewModel.loadMore();


            }
        }, 1200);
    }

    @Override
    public void complete() {
        mMainBinding.recylerView.setLoadingComplete();
        mMainBinding.recylerView.setRefreshComplete();
    }

    @Override
    public void onRefresh() {
        mMainBinding.recylerView.setRefreshEnable(true);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mMainViewModel.refresh();

            }
        }, 1200);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mMainViewModel != null) {
            mMainViewModel.detachView();
        }
    }
}
activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">


    <LinearLayout
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:orientation="vertical">


        <com.mrgao.luckrecyclerview.LucklyRecyclerView
            android:layout_width="match_parent"
            android:id="@+id/recylerView"
            android:layout_height="match_parent">

        </com.mrgao.luckrecyclerview.LucklyRecyclerView>
    </LinearLayout>
</layout>
  • 适配器的编写
/**
 * Created by mr.gao on 2018/4/6.
 * Package:    com.gao.mvvmdemo.mvvm.adapter
 * Create Date:2018/4/6
 * Project Name:MVVMDemo
 * Description:
 */

public class MainDataAdapter extends BaseBindingAdapter<ShowDataBeans,BaseBindViewHolder> {


    public MainDataAdapter(Context context) {
        super(context);
    }

    @Override
    public BaseBindViewHolder onCreateVH(ViewGroup parent, int viewType) {
        ViewDataBinding binding=DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.main_item,parent,false);
        return new BaseBindViewHolder(binding);
    }


    @Override
    public void onBindVH(BaseBindViewHolder holder, int position) {
        ViewDataBinding binding=holder.getBinding();
        binding.setVariable(BR.databeans,mDataList.get(position));//绑定数据
        binding.executePendingBindings();//防止闪烁
    }
}
binding.setVariable(BR.databeans,mDataList.get(position));//绑定数据
        binding.executePendingBindings();//防止闪烁
    }
}

.main_item

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:bind="http://schemas.android.com/tools"
    >
    <data>
        <variable
            name="databeans"
            type="com.gao.mvvmdemo.mvvm.beans.ShowDataBeans"/>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:padding="10dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal">


        <ImageView
            android:layout_width="80dp"
            bind:imageUrl="@{databeans.images[0]}"
            bind:error="@{@drawable/ic_launcher}"
            android:layout_height="80dp"/>


        <LinearLayout
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:layout_height="match_parent">
            <TextView
                android:layout_width="wrap_content"
                android:textStyle="bold"
                android:text="@{databeans.source}"
                android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_marginTop="10dp"
                android:text="@{databeans.desc}"
                android:layout_height="wrap_content"/>
        </LinearLayout>

    </LinearLayout>

</layout>bind:imageUrl="@{databeans.images[0]}"
            bind:error="@{@drawable/ic_launcher}"
            android:layout_height="80dp"/>


        <LinearLayout
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:layout_height="match_parent">
            <TextView
                android:layout_width="wrap_content"
                android:textStyle="bold"
                android:text="@{databeans.source}"
                android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_marginTop="10dp"
                android:text="@{databeans.desc}"
                android:layout_height="wrap_content"/>
        </LinearLayout>

    </LinearLayout>

</layout>

上面标红的部分使用的是自定义的加载图片方法:

public class ImageHelper {

    /**
     * 注意:第三个参数为图片显示错误的时候,此处为drawable对象,
     * 那么在xml文件中就应该为@drawable/xxxx,假设此处使用errorDra为int类型则会报错:
     * Error:(22, 29) Cannot find the setter for attribute 'app:imageUrl' with para
     * @param imageView
     * @param url
     * @param errorDra
     */
    @BindingAdapter(value = {"bind:imageUrl","bind:error"})
    public static void loadImages(ImageView imageView,String url,Drawable errorDra){
        Glide.with(imageView.getContext()).load(url).error(errorDra).centerCrop().into(imageView);
    }
}
 

第三部分:Model中使用retrofit+httpclient的方式访问数据

    访问数据请在AndroidManifest.xml中添加网络权限    

    此处有RxJava2.0相关的知识就不讲解咯疑问, 。

  •   HttpUtils:访问前肯定的做一些配置吧,比如超时重连,连接的时长等。此处的create使用的泛型,是为了复用,在请求URL的时候我们只需要传递一个class即可
public class HttpsUtils {
    OkHttpClient mClient;
    private static HttpsUtils sHttpsUtils;
    private static int TIME_OUT = 10;
    private Retrofit mRetrofit = null;

    public static HttpsUtils getInstance() {
        if (sHttpsUtils == null) {
            synchronized (HttpsUtils.class) {
                if (sHttpsUtils == null) {
                    sHttpsUtils = new HttpsUtils();
                }
            }
        }

        return sHttpsUtils;
    }

    public HttpsUtils() {
        initOkhttp();
        getRetrofit();
    }

    private void initOkhttp() {
        mClient = new OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
                .readTimeout(TIME_OUT, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .writeTimeout(TIME_OUT, TimeUnit.SECONDS)
                .build();
    }

    private Retrofit getRetrofit() {
        if (mRetrofit == null) {
            mRetrofit = new Retrofit.Builder()
                    .client(mClient)
                    .baseUrl(HttpURLS.BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
        }
        return mRetrofit;
    }


    /**
     * 首先的创建
     * @param tClass
     * @param <T>
     * @return
     */
    public <T> T create(Class<T> tClass) {
        return mRetrofit.create(tClass);
    }

}
<strong>访问数据的接口:MainAPI</strong>
public interface MainAPI {

    @GET("Android/10/{page}")
    Observable<DataBeans> getAndroidAPI(@Path("page") int page);
}
  • 在Model中获取数据
public class BaseMainModelImpl implements BaseMainModel {
    private static String TAG = "BaseMainModelImpl";
    private List<ShowDataBeans> mShowDataBeans = new ArrayList<>();

    @Override
    public void getAndroidAPIData(int page, final MainDataInterface dataInterface) {
        HttpsUtils.getInstance()
                .create(MainAPI.class)
                .getAndroidAPI(page)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<DataBeans>() {
                    @Override
                    public void onNext(DataBeans beans) {
                        Log.i(TAG, "onNext");
                        if (!beans.isError()) {
                            mShowDataBeans.clear();
                            for (DataBeans.ResultsBean re : beans.getResults()) {
                                ShowDataBeans dataBeans=new ShowDataBeans();
                                dataBeans.set_id(re.get_id());
                                dataBeans.setImages(re.getImages());
                                dataBeans.setDesc(re.getDesc());
                                dataBeans.setSource(re.getSource());
                                mShowDataBeans.add(dataBeans);
                            }
                        }
                        dataInterface.success(mShowDataBeans);
                    }

                    @Override
                    public void onComplete() {
                        Log.i(TAG, "onComplete");
                        dataInterface.complete();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i(TAG, "onError");
                        dataInterface.error(e.getMessage());
                    }

                    @Override
                    public void onSubscribe(Disposable d) {

                    }
                });
    }
}

第四部分:也就是View和Model的桥梁部分(ViewModel)

        ViewModel起的主要作用是协调View和Model的数据绑定。

注意:此处的MainViewModelImpl继承与BaseVM,BaseVM在前面部分介绍过,使用attachView和detachView,getIView的作用,传入的泛型为MainActivity,那么在获取数据完成之后或者是报错的时候直接使用getIView().complete()/error()的方式传递给activity,然后再activity中显示提示给用户,从而达到view-viewmodel -model的解耦。

public class MainViewModelImpl extends BaseVM<MainActivity> implements BaseMainVM, MainDataInterface {
    private BaseMainModelImpl mMainModel;
    private MainDataAdapter mDataAdapter;
    private int page=1;
    public MainViewModelImpl() {
        mMainModel=new BaseMainModelImpl();
    }


    public void setDataAdapter(MainDataAdapter adapter){
        mDataAdapter=adapter;
    }

    @Override
    public void refresh() {
        page=1;
        mMainModel.getAndroidAPIData(1,this);
    }

    @Override
    public void loadMore() {
        page++;
        mMainModel.getAndroidAPIData(page,this);

    }

    @Override
    public void success(List<ShowDataBeans> beansList) {

        if (page==1){
            mDataAdapter.onRefreshData(beansList);
        }else {
            mDataAdapter.onLoadMoreData(beansList);
        }
    }

    @Override
    public void error(String message) {
        getIView().showError(message);
    }

    @Override
    public void complete() {
        getIView().complete();
    }
}

源码请见:

MVVMDemo的源码

欢迎大家关注我的微信公众号:

猜你喜欢

转载自blog.csdn.net/qq_32400821/article/details/79836788
今日推荐