智能厨房重构-MVP架构

上一篇博客智能厨房-项目分包,我们介绍了项目分包的结构,这一篇我们重点来介绍一下MVP架构在项目中的应用,MVP可以说是MVC模式的一种升级,在MVP出现之前,一般都是用MVC,但是使用MVC有一个很大的缺点就是:通常将Activity作为Controller,xml文件当做View,Model数据请求类不变,但是Activity作为展示界面,通常要处理点击事件的响应,这就将C和V耦合在了一起,造成的后果就是activity动不动就大篇幅的代码,后期难以维护和升级。自从用了MVP之后,妈妈再也不要担心我的Activity类爆炸了。

1.MVP的简单介绍

这里写图片描述

View:是视图,用户界面,一般都是Activity充当这个角色,一般就是响应点击事件和加载数据到视图上。

Model:是数据处理,一般获取数据库数据、网络请求都定义在这里面。

Presenter:是组织者,是真正的执行任务的地方,一般一个presenter里面有一个View和Model的引用。

talk is cheap,让我来看一下实例。

2.MVP的实例运用

先说一下需求,假如我们现在要动态解析http://www.meishij.net/list.php?sortby=update&lm=369&page=1
美食杰往上面的数据,如何加载到我们的App上。

这里写图片描述

变成这样。

这里写图片描述

想想还是不错的,靠偷别人网上的数据就可以形成自己的数据接口了,哈哈。

先看下数据格式:

/*** 作者:GXL on 2016/8/3 0003* 博客: http://blog.csdn.net/u014316462* 作用:缩略展示美食,外部listview使用*/import java.io.Serializable;
public class FoodGeneralItem implements Serializable{/*** 标题*/private String title;/*** 链接**/private String link;/*** 图片的链接*/private String imgLink;/*** 作者名*/private String writer;public String getTaste() {return taste;}public void setTaste(String taste) {this.taste = taste;}/*** 味道*/private String taste;public String getDiscuss() {return discuss;}public void setDiscuss(String discuss) {this.discuss = discuss;}/*** 评论*/private String discuss;public String getTitle() {return title;}public String getDate() {return date;}public void setDate(String date) {this.date = date;}/***  步骤 时间*/private String date;public void setTitle(String title) {this.title = title;}public String getLink() {return link;}public void setLink(String link) {this.link = link;}public String getImgLink() {return imgLink;}public void setImgLink(String imgLink) {this.imgLink = imgLink;}public String getWriter() {return writer;}public void setWriter(String writer) {this.writer = writer;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn title;}
}

这没什么说的,就是需要展示的一些数据。我们看关键的MVP用法。先给一个类图给一个直观的认识。

这里写图片描述

下面说明一下:

View部分

通常一个功能界面对应一个View接口,接口里面定义数据加载View中方法,比如:将上面的数据集合加载到
listview中就可以定义一个接口方法,看一下IFoodFragment的实现。

/*** 作者:GXL on 2016/8/3 0003* 博客: http://blog.csdn.net/u014316462* 作用:FoodFragment的View接口*/
public interface IFoodFragment {//加载更多void onLoadMore(List list);//下拉刷新void onRefresh(List list);//初始化sliderViewvoid onInitSliderShow(List list);
}

还是很清晰的,下拉刷新的处理函数、上拉加载更多的处理函数、初始化一个SliderView函数,这是sliderView是一个类似广告位滚动的自定义View,后面会写一篇博文介绍一下。看一下IFoodFragment在FoodFragment的实现。

*** 作者:GXL on 2016/8/3 0003* 博客: http://blog.csdn.net/u014316462* 作用:展示美食的页面*/
public class FoodFragment extends Fragment implements IFoodFragment, XListView.IXListViewListener {@Bind(R.id.xListView)XListView xListView;@Bind(R.id.menu)ImageView menu;@Bind(R.id.search)EditText search;@Bind(R.id.loading)RelativeLayout loading;@Bind(R.id.tip)LinearLayout tip;private int[] mIconList = {R.drawable.cate_jiachang_5, R.drawable.cate_zhonghua_5,R.drawable.cate_xiaochi_5, R.drawable.cate_waiguo_5,R.drawable.cate_hongbei_5, R.drawable.cate_renqun_5};private String[] mNameList = {"家常菜谱", "中华菜系", "各地小吃", "外国菜谱", "烘焙", "我的收藏"};private FoodGeneralAdapter mAdapter;private LinearLayout mFoodFragmentHead;private List> mDataList;private ArrayList mFoodGeneralList;private SimpleAdapter mSimpleAdapter;private FoodFragmentPresenter mFoodFragmentPresenter;private int mPage = 1;private GridView mGridview;private SlideShowView mSlideshowView;private final String  FOODITEM="fooditem";public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View v = inflater.inflate(R.layout.foodfragment_main, container, false);ButterKnife.bind(this, v);mFoodFragmentHead = (LinearLayout) inflater.inflate(R.layout.foodfragment_head, null);mGridview = (GridView) mFoodFragmentHead.findViewById(R.id.gridview);mSlideshowView = (SlideShowView) mFoodFragmentHead.findViewById(R.id.slideshowView);xListView.addHeaderView(mFoodFragmentHead);init();return v;}private void init() {mFoodGeneralList = new ArrayList<>();mAdapter = new FoodGeneralAdapter(getActivity(), mFoodGeneralList);xListView.setAdapter(mAdapter);xListView.setPullLoadEnable(true);xListView.setXListViewListener(this);mDataList = new ArrayList>();initData();String[] from = {"image", "text"};int[] to = {R.id.image, R.id.text};mSimpleAdapter = new SimpleAdapter(getActivity(), mDataList,R.layout.gridview_item, from, to);mGridview.setAdapter(mSimpleAdapter);mFoodFragmentPresenter = new FoodFragmentPresenter(this);if(NetUtil.checkNet(getActivity())) {mFoodFragmentPresenter.onInitSliderShow();mFoodFragmentPresenter.onRefresh("update", 13, 1);}else{Log.i("TAG", "init: "+"meiwang");loading.setVisibility(View.GONE);tip.setVisibility(View.VISIBLE);xListView.setVisibility(View.GONE);}mGridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView parent, View view,int position, long id) {{Intent intent = new Intent(getActivity(),FoodTypeActivity.class);Bundle bundle = new Bundle();bundle.putInt("position", position);intent.putExtras(bundle);startActivity(intent);}}});xListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView parent, View view, int position, long id) {List mDatas = mAdapter.returnmDatas();FoodGeneralItem fooditem = mDatas.get(position - 2);Intent intent = new Intent(getActivity(), FoodTeachActivity.class);intent.putExtra("URLLINK",fooditem.getLink());startActivity(intent);}});}@Overridepublic void onDestroyView() {super.onDestroyView();ButterKnife.unbind(this);}@Overridepublic void onLoadMore(List list) {mAdapter.addAll(list);mAdapter.notifyDataSetChanged();xListView.stopLoadMore();}@Overridepublic void onRefresh(List list) {loading.setVisibility(View.GONE);tip.setVisibility(View.GONE);xListView.setVisibility(View.VISIBLE);mAdapter.setDatas(list);mAdapter.notifyDataSetChanged();xListView.stopRefresh();}@Overridepublic void onInitSliderShow(List list) {mSlideshowView.initUI(getActivity(), list);}@Overridepublic void onRefresh() {mFoodFragmentPresenter.onRefresh("update", 13, 1);}@Overridepublic void onLoadMore() {mPage++;mFoodFragmentPresenter.onLoadMore("update", 13, mPage);}public void initData() {for (int i = 0; i < mIconList.length; i++) {Map map = new HashMap();map.put("image", mIconList[i]);map.put("text", mNameList[i]);mDataList.add(map);}}@OnClick({R.id.menu, R.id.search})public void onClick(View view) {switch (view.getId()) {case R.id.menu:break;case R.id.search:break;}}
}

关键看一下实现的三个方法理解一下。Activity中都需要一个Presenter对象,用来页面响应时真正的处理请求。

Model部分

先看一下FoodModelImpl接口的实现

/*** 作者:GXL on 2016/8/3 0003* 博客: http://blog.csdn.net/u014316462* 作用:FoodModel接口*/
public interface FoodModelImpl {/*** 获取美食的大概信息* @param sortby* @param type* @param page* @param listener*/void getGeneralFoodsItem(String sortby,int type,int page,BaseListener listener);/*** 获取美食的详细做法* @param foodlink* @param listener*/void getFoodDetailTeachItem(String foodlink,BaseListener listener);/*** 获取滚动展示的数据集合* @param listener*/void  getSliderShowFood(BaseListener listener);interface BaseListener{void getSuccess(T t);void getFailure();}
}

在看一下实现,和上面的View的思想差不多,请求一类数据定义一个接口。

/*** 作者:GXL on 2016/8/3 0003* 博客: http://blog.csdn.net/u014316462* 作用:数据请求Model*/
public class FoodModel implements FoodModelImpl {@Overridepublic void getGeneralFoodsItem(String sortby, int type, int page, final BaseListener listener) {FoodService service = RetrofitWrapper.getInstance().create(FoodService.class);service.getFoodList(sortby,type,page).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(new Func1>(){@Overridepublic List call(String s) {return HtmlParser.parserHtml(s);}}).subscribe(new Subscriber>() {@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {listener.getFailure();}@Overridepublic void onNext(List s) {listener.getSuccess(s);}});}@Overridepublic void getFoodDetailTeachItem(String foodlink, final BaseListener listener) {String foodname=foodlink.substring(foodlink.lastIndexOf("/")+1,foodlink.lastIndexOf("."));FoodService service = RetrofitWrapper.getInstance().create(FoodService.class);service.getDetailFood(foodname).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(new Func1() {@Overridepublic FoodDetailTeachItem call(String s) {return HtmlParser.parserHtmlToDetailInPc(s);}}).subscribe(new Subscriber() {@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {listener.getFailure();}@Overridepublic void onNext(FoodDetailTeachItem foodDetailTeachItem) {listener.getSuccess(foodDetailTeachItem);}});}@Overridepublic void getSliderShowFood(final BaseListener listener) {FoodService service = RetrofitWrapper.getInstance().create(FoodService.class);service.getSliderShowFood().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(new Func1>() {@Overridepublic List call(String s) {return HtmlParser.parserHtmlToSliderShow(s);}}).subscribe(new Subscriber< List>() {@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {listener.getFailure();}@Overridepublic void onNext(List sliderShowViewItems) {listener.getSuccess(sliderShowViewItems);}});}
}

看到这个数据请求代码,你可能会感到很惊讶,特别简捷,这就是用了RxJava和Retrofit的好处了,下一篇我会重点讲一下他们的应用。

Presenter部分

前面View和Model都定义好了,老大也是时候出场了,为什么说它是老大尼?是因为大部分事情都是Presenter协调View和Model去处理的。看一下代码:

/*** 作者:GXL on 2016/8/3 0003* 博客: http://blog.csdn.net/u014316462* 作用:FoodFragment的Presenter*/
public class FoodFragmentPresenter {private IFoodFragment mIFoodFragment;private FoodModel mFoodModel=new FoodModel();public FoodFragmentPresenter(IFoodFragment mIFoodFragment) {this.mIFoodFragment = mIFoodFragment;}/*** 上拉加载更多* @param sortby* @param lm* @param page*/public void onLoadMore(String sortby,int lm,int page){mFoodModel.getGeneralFoodsItem(sortby, lm, page, new FoodModelImpl.BaseListener() {@Overridepublic void getSuccess(Object o) {List list= (List) o;mIFoodFragment.onLoadMore(list);}@Overridepublic void getFailure() {}});}/*** 下拉刷新* @param sortby* @param lm* @param page*/public void onRefresh(String sortby,int lm,int page){mFoodModel.getGeneralFoodsItem(sortby, lm, page, new FoodModelImpl.BaseListener() {@Overridepublic void getSuccess(Object o) {List list= (List) o;mIFoodFragment.onRefresh(list);}@Overridepublic void getFailure() {}});}/*** 初始化SliderShow*/public void onInitSliderShow(){mFoodModel.getSliderShowFood(new FoodModelImpl.BaseListener() {@Overridepublic void getSuccess(Object o) {List list= (List) o;for (SlideShowView.SliderShowViewItem item:list) {Log.i("TAG", "onInitSliderShow: "+item.getFoodname());}mIFoodFragment.onInitSliderShow(list);}@Overridepublic void getFailure() {}});}
}

从代码中,我们可以清楚的看见,FoodFragmentPresenter里面包含了IFoodFragment的View接口和FoodModel数据请求接口。响应事件时就可以:FoodPresenter的某个函数被响应——–》让FoodModel去获取数据——》数据获取到了让View去加载数据。

还是从一个真实的流程看一下:
比如现在下拉Listview刷新。

1.FoodFragment的listview响应onRefresh()中执行Presenter的onRefresh方法。

  public void onRefresh() {mFoodFragmentPresenter.onRefresh("update", 13, 1);}
  1. mFoodFragmentPresenter.onRefresh(“update”,13,1)方法中会先调用Model进行数据请求,成功后会在回调中将数据加载到View接口中。这样就完成了数据的加载。
 /*** 下拉刷新* @param sortby* @param lm* @param page*/public void onRefresh(String sortby,int lm,int page){mFoodModel.getGeneralFoodsItem(sortby, lm, page, new FoodModelImpl.BaseListener() {@Overridepublic void getSuccess(Object o) {List list= (List) o;mIFoodFragment.onRefresh(list);}@Overridepublic void getFailure() {}});}

多看两遍就理解这个流程了,项目代码地址:https://github.com/gxl1240779189/ReIntelligentKitchen,我最近我慢慢完善,争取将最近的技术融入进去,欢迎star一起交流。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部