智能厨房重构-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;
    }

    @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);
    }
  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一起交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值