上一篇博客智能厨房-项目分包,我们介绍了项目分包的结构,这一篇我们重点来介绍一下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;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return 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<FoodGeneralItem> list);
//下拉刷新
void onRefresh(List<FoodGeneralItem> list);
//初始化sliderView
void onInitSliderShow(List<SlideShowView.SliderShowViewItem> 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<Map<String, Object>> mDataList;
private ArrayList<FoodGeneralItem> 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<Map<String, Object>>();
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() {
@Override
public 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() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
List<FoodGeneralItem> mDatas = mAdapter.returnmDatas();
FoodGeneralItem fooditem = mDatas.get(position - 2);
Intent intent = new Intent(getActivity(), FoodTeachActivity.class);
intent.putExtra("URLLINK",fooditem.getLink());
startActivity(intent);
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
ButterKnife.unbind(this);
}
@Override
public void onLoadMore(List<FoodGeneralItem> list) {
mAdapter.addAll(list);
mAdapter.notifyDataSetChanged();
xListView.stopLoadMore();
}
@Override
public void onRefresh(List<FoodGeneralItem> list) {
loading.setVisibility(View.GONE);
tip.setVisibility(View.GONE);
xListView.setVisibility(View.VISIBLE);
mAdapter.setDatas(list);
mAdapter.notifyDataSetChanged();
xListView.stopRefresh();
}
@Override
public void onInitSliderShow(List<SlideShowView.SliderShowViewItem> list) {
mSlideshowView.initUI(getActivity(), list);
}
@Override
public void onRefresh() {
mFoodFragmentPresenter.onRefresh("update", 13, 1);
}
@Override
public void onLoadMore() {
mPage++;
mFoodFragmentPresenter.onLoadMore("update", 13, mPage);
}
public void initData() {
for (int i = 0; i < mIconList.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
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<T>{
void getSuccess(T t);
void getFailure();
}
}
在看一下实现,和上面的View的思想差不多,请求一类数据定义一个接口。
/**
* 作者:GXL on 2016/8/3 0003
* 博客: http://blog.csdn.net/u014316462
* 作用:数据请求Model
*/
public class FoodModel implements FoodModelImpl {
@Override
public 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<String, List<FoodGeneralItem>>(){
@Override
public List<FoodGeneralItem> call(String s) {
return HtmlParser.parserHtml(s);
}
}).subscribe(new Subscriber<List<FoodGeneralItem>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.getFailure();
}
@Override
public void onNext(List<FoodGeneralItem> s) {
listener.getSuccess(s);
}
});
}
@Override
public 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<String, FoodDetailTeachItem>() {
@Override
public FoodDetailTeachItem call(String s) {
return HtmlParser.parserHtmlToDetailInPc(s);
}
})
.subscribe(new Subscriber<FoodDetailTeachItem>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.getFailure();
}
@Override
public void onNext(FoodDetailTeachItem foodDetailTeachItem) {
listener.getSuccess(foodDetailTeachItem);
}
});
}
@Override
public void getSliderShowFood(final BaseListener listener) {
FoodService service = RetrofitWrapper.getInstance().create(FoodService.class);
service.getSliderShowFood().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.map(new Func1<String, List<SlideShowView.SliderShowViewItem>>() {
@Override
public List<SlideShowView.SliderShowViewItem> call(String s) {
return HtmlParser.parserHtmlToSliderShow(s);
}
})
.subscribe(new Subscriber< List<SlideShowView.SliderShowViewItem>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.getFailure();
}
@Override
public void onNext(List<SlideShowView.SliderShowViewItem> 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() {
@Override
public void getSuccess(Object o) {
List<FoodGeneralItem> list= (List<FoodGeneralItem>) o;
mIFoodFragment.onLoadMore(list);
}
@Override
public 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() {
@Override
public void getSuccess(Object o) {
List<FoodGeneralItem> list= (List<FoodGeneralItem>) o;
mIFoodFragment.onRefresh(list);
}
@Override
public void getFailure() {
}
});
}
/**
* 初始化SliderShow
*/
public void onInitSliderShow(){
mFoodModel.getSliderShowFood(new FoodModelImpl.BaseListener() {
@Override
public void getSuccess(Object o) {
List<SlideShowView.SliderShowViewItem> list= (List<SlideShowView.SliderShowViewItem>) o;
for (SlideShowView.SliderShowViewItem item:list
) {
Log.i("TAG", "onInitSliderShow: "+item.getFoodname());
}
mIFoodFragment.onInitSliderShow(list);
}
@Override
public void getFailure() {
}
});
}
}
从代码中,我们可以清楚的看见,FoodFragmentPresenter里面包含了IFoodFragment的View接口和FoodModel数据请求接口。响应事件时就可以:FoodPresenter的某个函数被响应——–》让FoodModel去获取数据——》数据获取到了让View去加载数据。
还是从一个真实的流程看一下:
比如现在下拉Listview刷新。
1.FoodFragment的listview响应onRefresh()中执行Presenter的onRefresh方法。
public void onRefresh() {
mFoodFragmentPresenter.onRefresh("update", 13, 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() {
@Override
public void getSuccess(Object o) {
List<FoodGeneralItem> list= (List<FoodGeneralItem>) o;
mIFoodFragment.onRefresh(list);
}
@Override
public void getFailure() {
}
});
}
多看两遍就理解这个流程了,项目代码地址:https://github.com/gxl1240779189/ReIntelligentKitchen,我最近我慢慢完善,争取将最近的技术融入进去,欢迎star一起交流。