什么是 MVP
在项目中 View 和 Model 并不直接交互,而是使用 Presenter 作为 View 和 Model 之间的桥梁。其中 Presenter 中同时持有 View 层以及 Model 层的 Interface 的引用,而 View 层持有 Presenter 层 Interface 的引用,当 View 层某个页面需要展示某些数据的时候,首先会调用Presenter 层的某个接口,然后 Presenter 层会调用 Model 层请求数据,当 Model 层数据加载成功之后会调用 Presenter 层的回调方法通知 Presenter 层数据加载完毕,最后 Presenter 层再调用 View 层的接口将加载后的数据展示给用户。这就是 MVP 模式的核心过程。
这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述。
这里就给大家随便看看干货板块的功能吧。
布局相当简单。
1 <?xml version="1.0" encoding="utf-8"?> 2 <android.support.v4.widget.SwipeRefreshLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 xmlns:app="http://schemas.android.com/apk/res-auto" 5 android:id="@+id/swipe_refresh_layout" 6 android:layout_width="match_parent" 7 android:layout_height="match_parent"> 8 9 10 <com.nanchen.aiyagirl.widget.RecyclerViewWithFooter.RecyclerViewWithFooter11 android:id="@+id/recyclerView"12 android:layout_width="match_parent"13 android:layout_height="match_parent"/>14 15 16 </android.support.v4.widget.SwipeRefreshLayout>
干货模块,也就是一个Fragment,里面有一个RecyclerView,支持下拉刷新和上拉加载数据。所以我们的 Presenter 和 View 只需要定义一下简单的方法。
1)加载数据的过程中显示加载的进度条;
2)加载数据成功提醒 Adapter 刷新数据;
3)加载失败谈窗提醒用户相关信息;
4)加载结束隐藏进度条;
1 package com.nanchen.aiyagirl.module.category; 2 3 import com.nanchen.aiyagirl.base.BasePresenter; 4 import com.nanchen.aiyagirl.base.BaseView; 5 import com.nanchen.aiyagirl.model.CategoryResult; 6 7 /** 8 * Author: nanchen 9 * Email: liushilin520@foxmail.com10 * Date: 2017-04-14 10:1411 */12 13 public interface CategoryContract {14 15 interface ICategoryView extends BaseView{16 17 void getCategoryItemsFail(String failMessage);18 19 void setCategoryItems(CategoryResult categoryResult);20 21 void addCategoryItems(CategoryResult categoryResult);22 23 void showSwipeLoading();24 25 void hideSwipeLoading();26 27 void setLoading();28 29 String getCategoryName();30 31 void noMore();32 }33 34 interface ICategoryPresenter extends BasePresenter{35 36 void getCategoryItems(boolean isRefresh);37 }38 }
编写 Presenter 实现类。
1 package com.nanchen.aiyagirl.module.category; 2 3 import com.nanchen.aiyagirl.config.GlobalConfig; 4 import com.nanchen.aiyagirl.model.CategoryResult; 5 import com.nanchen.aiyagirl.module.category.CategoryContract.ICategoryView; 6 import com.nanchen.aiyagirl.module.category.CategoryContract.ICategoryPresenter; 7 import com.nanchen.aiyagirl.net.NetWork; 8 9 import rx.Observer;10 import rx.Subscription;11 import rx.android.schedulers.AndroidSchedulers;12 import rx.schedulers.Schedulers;13 14 /**15 * ICategoryPresenter16 * <p>17 * Author: nanchen18 * Email: liushilin520@foxmail.com19 * Date: 2017-04-14 11:1620 */21 22 public class CategoryPresenter implements ICategoryPresenter {23 24 private ICategoryView mCategoryICategoryView;25 private int mPage = 1;26 private Subscription mSubscription;27 28 public CategoryPresenter(ICategoryView androidICategoryView) {29 mCategoryICategoryView = androidICategoryView;30 }31 32 @Override33 public void subscribe() {34 getCategoryItems(true);35 }36 37 @Override38 public void unSubscribe() {39 if (mSubscription != null && !mSubscription.isUnsubscribed()){40 mSubscription.unsubscribe();41 }42 }43 44 @Override45 public void getCategoryItems(final boolean isRefresh) {46 if (isRefresh) {47 mPage = 1;48 mCategoryICategoryView.showSwipeLoading();49 } else {50 mPage++;51 }52 mSubscription = NetWork.getGankApi()53 .getCategoryData(mCategoryICategoryView.getCategoryName(), GlobalConfig.CATEGORY_COUNT,mPage)54 .subscribeOn(Schedulers.io())55 .observeOn(AndroidSchedulers.mainThread())56 .subscribe(new Observer<CategoryResult>() {57 @Override58 public void onCompleted() {59 60 }61 62 @Override63 public void onError(Throwable e) {64 mCategoryICategoryView.hideSwipeLoading();65 mCategoryICategoryView.getCategoryItemsFail(mCategoryICategoryView.getCategoryName()+" 列表数据获取失败!");66 }67 68 @Override69 public void onNext(CategoryResult categoryResult) {70 if (isRefresh){71 mCategoryICategoryView.setCategoryItems(categoryResult);72 mCategoryICategoryView.hideSwipeLoading();73 mCategoryICategoryView.setLoading();74 }else {75 mCategoryICategoryView.addCategoryItems(categoryResult);76 }77 }78 });79 80 }81 }
编写Adapter,用于展示数据。
1 package com.nanchen.aiyagirl.module.category; 2 3 import android.content.Context; 4 import android.content.Intent; 5 import android.view.View; 6 import android.widget.ImageView; 7 8 import com.bumptech.glide.Glide; 9 import com.nanchen.aiyagirl.ConfigManage;10 import com.nanchen.aiyagirl.R;11 import com.nanchen.aiyagirl.base.adapter.CommonRecyclerAdapter;12 import com.nanchen.aiyagirl.base.adapter.CommonRecyclerHolder;13 import com.nanchen.aiyagirl.base.adapter.ListenerWithPosition;14 import com.nanchen.aiyagirl.model.CategoryResult;15 import com.nanchen.aiyagirl.model.CategoryResult.ResultsBean;16 import com.nanchen.aiyagirl.module.web.WebViewActivity;17 import com.nanchen.aiyagirl.utils.TimeUtil;18 19 /**20 * Author: nanchen21 * Email: liushilin520@foxmail.com22 * Date: 2017-04-14 10:2123 */24 25 class CategoryRecyclerAdapter extends CommonRecyclerAdapter<CategoryResult.ResultsBean> implements ListenerWithPosition.OnClickWithPositionListener<CommonRecyclerHolder>{26 27 CategoryRecyclerAdapter(Context context) {28 super(context, null, R.layout.item_category);29 }30 31 @Override32 public void convert(CommonRecyclerHolder holder, ResultsBean resultsBean) {33 if (resultsBean != null) {34 ImageView imageView = holder.getView(R.id.category_item_img);35 if (ConfigManage.INSTANCE.isListShowImg()) { // 列表显示图片36 imageView.setVisibility(View.VISIBLE);37 String quality = "";38 if (resultsBean.images != null && resultsBean.images.size() > 0) {39 switch (ConfigManage.INSTANCE.getThumbnailQuality()) {40 case 0: // 原图41 quality = "";42 break;43 case 1: //44 quality = "?imageView2/0/w/400";45 break;46 case 2:47 quality = "?imageView2/0/w/190";48 break;49 }50 Glide.with(mContext)51 .load(resultsBean.images.get(0) + quality)52 .placeholder(R.mipmap.image_default)53 .error(R.mipmap.image_default)54 .into(imageView);55 } else { // 列表不显示图片56 Glide.with(mContext).load(R.mipmap.image_default).into(imageView);57 }58 } else {59 imageView.setVisibility(View.GONE);60 }61 62 holder.setTextViewText(R.id.category_item_desc, resultsBean.desc == null ? "unknown" : resultsBean.desc);63 holder.setTextViewText(R.id.category_item_author, resultsBean.who == null ? "unknown" : resultsBean.who);64 holder.setTextViewText(R.id.category_item_time, TimeUtil.dateFormat(resultsBean.publishedAt));65 holder.setTextViewText(R.id.category_item_src, resultsBean.source == null ? "unknown" : resultsBean.source);66 holder.setOnClickListener(this, R.id.category_item_layout);67 }68 }69 70 @Override71 public void onClick(View v, int position, CommonRecyclerHolder holder) {72 // Toasty.info(mContext,"跳转到相应网页!", Toast.LENGTH_SHORT,true).show();73 Intent intent = new Intent(mContext, WebViewActivity.class);74 intent.putExtra(WebViewActivity.GANK_TITLE, mData.get(position).desc);75 intent.putExtra(WebViewActivity.GANK_URL, mData.get(position).url);76 mContext.startActivity(intent);77 }78 }
最后当然是 Fragment。
package com.nanchen.aiyagirl.module.category;import android.os.Bundle;import android.support.v4.widget.SwipeRefreshLayout;import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;import android.support.v7.widget.LinearLayoutManager;import com.nanchen.aiyagirl.R;import com.nanchen.aiyagirl.base.BaseFragment;import com.nanchen.aiyagirl.model.CategoryResult;import com.nanchen.aiyagirl.module.category.CategoryContract.ICategoryPresenter;import com.nanchen.aiyagirl.module.category.CategoryContract.ICategoryView;import com.nanchen.aiyagirl.widget.RecyclerViewDivider;import com.nanchen.aiyagirl.widget.RecyclerViewWithFooter.OnLoadMoreListener;import com.nanchen.aiyagirl.widget.RecyclerViewWithFooter.RecyclerViewWithFooter;import butterknife.BindView;import es.dmoral.toasty.Toasty;/** * 主页轮播下面的Fragment * <p> * Author: nanchen * Email: liushilin520@foxmail.com * Date: 2017-04-14 9:46 */public class CategoryFragment extends BaseFragment implements ICategoryView, OnRefreshListener, OnLoadMoreListener { public static final String CATEGORY_NAME = "com.nanchen.aiyagirl.module.category.CategoryFragment.CATEGORY_NAME"; @BindView(R.id.recyclerView) RecyclerViewWithFooter mRecyclerView; @BindView(R.id.swipe_refresh_layout) SwipeRefreshLayout mSwipeRefreshLayout; private String categoryName; private CategoryRecyclerAdapter mAdapter; private ICategoryPresenter mICategoryPresenter; public static CategoryFragment newInstance(String mCategoryName) { CategoryFragment categoryFragment = new CategoryFragment(); Bundle bundle = new Bundle(); bundle.putString(CATEGORY_NAME, mCategoryName); categoryFragment.setArguments(bundle); return categoryFragment; } @Override protected int getContentViewLayoutID() { return R.layout.fragment_category; } @Override protected void init() { mICategoryPresenter = new CategoryPresenter(this); categoryName = getArguments().getString(CATEGORY_NAME); mSwipeRefreshLayout.setOnRefreshListener(this); mAdapter = new CategoryRecyclerAdapter(getActivity()); mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); mRecyclerView.addItemDecoration(new RecyclerViewDivider(getActivity(), LinearLayoutManager.HORIZONTAL)); mRecyclerView.setAdapter(mAdapter); mRecyclerView.setOnLoadMoreListener(this); mRecyclerView.setEmpty(); mICategoryPresenter.subscribe(); } @Override public void onDestroy() { super.onDestroy(); if (mICategoryPresenter != null) { mICategoryPresenter.unSubscribe(); } } @Override public void onRefresh() { mICategoryPresenter.getCategoryItems(true); } @Override public void onLoadMore() { mICategoryPresenter.getCategoryItems(false); } @Override public void getCategoryItemsFail(String failMessage) { if (getUserVisibleHint()) { Toasty.error(this.getContext(), failMessage).show(); } } @Override public void setCategoryItems(CategoryResult categoryResult) { mAdapter.setData(categoryResult.results); } @Override public void addCategoryItems(CategoryResult categoryResult) { mAdapter.addData(categoryResult.results); } @Override public void showSwipeLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override public void hideSwipeLoading() { mSwipeRefreshLayout.setRefreshing(false); } @Override public void setLoading() { mRecyclerView.setLoading(); } @Override public String getCategoryName() { return this.categoryName; } @Override public void noMore() { mRecyclerView.setEnd("没有更多数据"); } }