使用MVP打造项目框架
来源:互联网 发布:java开发简历模板 编辑:程序博客网 时间:2024/05/02 02:24
前言
在目前的项目框架中大多是用Viewpager+Fragment实现,而通常情况下一个Fragment中包含以下功能,但是如果将这些功能全部集成在一个fragment中会造成,逻辑不清楚,而且我们编写的时候本身也不容易理清顺序,比如在刷新界面的时候要分多种情况,如果是加载第一页且没有缓存数据的时候显示进度动画,否则显示listview自带的下拉刷新动画,当发生错误的时候也要根据有无显示的内容做相应的判断,如果有内容则显示Toast提示用户,否则切换到重试页面。如果将这些逻辑处理与fragment捆绑的话,如果需要更换banner控件,或则下拉刷新控件,则这些逻辑不能复用。所以我尝试使用MVP的方式实现项目框架。
功能
先看一下我目前实现的功能有哪些。
1.下拉刷新,上拉加载更多
2.懒加载,加载动画,数据缓存
3.错误提示(此处有数据,无法显示重试界面,不过如果没有数据的话是有重试界面的)
4.下一页重试,(因为已经有了一页数据,所以不能显示重试界面,否则会把用户的数据覆盖,此处添加了一个FooterView用于重试)
实现
1.为什么选择MVP
MVP(mode view presenter)与传统的MVC(mode view controller)更注重了view的独立性,在mvc中更注重model的独立性,即model是不变的而有多个view对应于同一个mode,这样就照成了view依赖mode,而view中不可避免的含义逻辑处理的部分,这样将照成view的重用性下降,系统的耦合性提高,而在mvp中mode 与 view 都是独立的。而主要的逻辑都在presenter中,而在presenter中持有的也是model与view的接口,所以这样就使得mode与view的重用性提高,系统的灵活性增加。
我的项目结构如下
2.接口抽取
这里面最关键的是对view接口的抽取,以前也看过许多人的mvpdemo但是等到自己写的时候却不知道要怎么办,我总结了一下对view接口抽取主要是三点
- 需要从view中获取的数据
- 需要让view显示的状态
- 需要让view执行的操作
IListView借口
package com.zgh.mvpdemo.view.news;import android.content.DialogInterface;import com.zgh.mvpdemo.bean.BannerItem;import com.zgh.mvpdemo.bean.NewsItem;import java.util.List;/** * Created by zhuguohui on 2016/6/29. */public interface IListView { /*******加载首页相关*******/ //显示加载首页的加载效果 void showLoadingFirstPage(); //隐藏加载首页动画 void hideLoadingFirstPage(); //首页加载失败的时候调用 void showRetryFirstPage(); //首页为空的时候调用 void showEmpty(); //获取到首页数据的时候调用 void showFirstPageData(List<NewsItem> listData); /***********加载下一页相关*************/ //显示加载下一页 void showLoadingNextPage(); //隐藏加载下一页 void hideLoadingNextPage(); //获取到下一页数据是调用 void showNextPageData(List<NewsItem> listData); //显示还有下一页 void showHaveNextPage(); //显示重试加载下一页 void showRetryNextPage(); //没有更多了 void showNoMore(); /**************Banner相关*********************/ //隐藏banner在没有banner数据的时候调用 void hideBanner(); //显示banner数据 void showBanner(List<BannerItem> bannerData); /*****************获取数据***************************/ //判断是否还有下一页 boolean haveNextPage(int dataSize); //是否已经有显示的内容了,用于判断在没有网络的时候是否显示重试界面,如果有内容则显示内容,否则显示重试。 boolean haveContent(); //获取首页的url地址 String getFirstPageUrl(); //下一页的url地址 String getNextPageUrl(); /**************点击事件****************************/ interface OnBannerClickListener { void onBannnerClick(BannerItem item); } interface OnNewsItemClickListener { void onNewsItemClick(NewsItem item); } void setOnBannerItemClickListener(OnBannerClickListener listener); void setOnListItemClickListener(OnNewsItemClickListener listener); void toItemDetail(NewsItem item); void toBannerDetail(BannerItem item); /****************通知*******************************/ void showToast(String info);}
IListMode 接口
package com.zgh.mvpdemo.model.news;/** * Created by zhuguohui on 2016/6/29. */public interface IListMode { /** * * @param useCache 是否使用缓存 * @param url 数据url * @param listener 回调接口 */ void LoadData(boolean useCache,String url,DataResultListener listener);}
对Presenter就没有抽取接口了
package com.zgh.mvpdemo.presenter.news;import com.zgh.mvpdemo.bean.BannerItem;import com.zgh.mvpdemo.bean.NewsItem;import com.zgh.mvpdemo.model.news.DataResultListener;import com.zgh.mvpdemo.model.news.IListMode;import com.zgh.mvpdemo.model.news.ListMode;import com.zgh.mvpdemo.view.news.IListView;import java.util.List;/** * Created by zhuguohui on 2016/6/29. */public class ListPresenter implements IListView.OnBannerClickListener, IListView.OnNewsItemClickListener { IListView listView; IListMode listMode; public ListPresenter(IListView listView) { this.listView = listView; listMode = new ListMode(); //设置点击事件 listView.setOnBannerItemClickListener(this); listView.setOnListItemClickListener(this); } public void LoadFirstPage(boolean useCache) { //在没有内容的时候才显示进度条,在下拉刷新的时候不需要 if(!listView.haveContent()) { listView.showLoadingFirstPage(); } listMode.LoadData(useCache,listView.getFirstPageUrl(), new DataResultListener() { @Override public void onSuccess(List<NewsItem> newsData, List<BannerItem> bannerData) { listView.hideLoadingFirstPage(); //如果banner数据不为空才显示banner,否则隐藏 if (bannerData != null && bannerData.size() > 0) { listView.showBanner(bannerData); } else { listView.hideBanner(); } //根据list是否有数据设置显示样式 if (newsData != null && newsData.size() > 0) { listView.showFirstPageData(newsData); //根据item的数量判断是否有下一页 if (listView.haveNextPage(newsData.size())) { listView.showHaveNextPage(); } else { listView.showNoMore(); } } else { listView.showEmpty(); } } @Override public void onError(String error) { listView.hideLoadingFirstPage(); //在没有内容的时候才显示重试,否则只提示,还是显示原来的缓存内容 if (!listView.haveContent()) { listView.showRetryFirstPage(); } listView.showToast(error); } }); } public void LoadNextPage() { //显示加载下一页 listView.showLoadingNextPage(); listMode.LoadData(false,listView.getNextPageUrl(), new DataResultListener() { @Override public void onSuccess(List<NewsItem> newsData, List<BannerItem> bannerData) { //在加载下一页的时候不需要判断banner的数据 if (newsData != null && newsData.size() > 0) { listView.showNextPageData(newsData); //如果没有下一页,则显示没有更多了 if (!listView.haveNextPage(newsData.size())) { listView.showNoMore(); }else{ listView.showHaveNextPage(); } } else { listView.showToast("没有更多了"); listView.showNoMore(); } listView.hideLoadingFirstPage(); } @Override public void onError(String error) { listView.hideLoadingNextPage(); //在没有内容的时候才显示重试,否则只提示,还是显示原来的缓存内容 if (!listView.haveContent()) { listView.showRetryFirstPage(); }else{ listView.showRetryNextPage(); } listView.showToast(error); } }); } @Override public void onBannnerClick(BannerItem item) { listView.toBannerDetail(item); } @Override public void onNewsItemClick(NewsItem item) { listView.toItemDetail(item); }}
3.状态切换
状态切换我使用的是张鸿洋的LoadingAndRetryManager 关于具体的用法大家自己去看吧。需要说明的是,LoadingAndRetryManager在ViewPager中的fragment中使用的时候要这么用,要将LoadingAndRetryLayout作为fragment的view返回,而第一个mBaseView是你自己创建的view。
mLoadingAndRetryManager = new LoadingAndRetryManager(mBaseView, new OnLoadingAndRetryListener() { @Override public void setRetryEvent(View retryView) { retryView.findViewById(R.id.id_btn_retry).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPresenter.LoadFirstPage(true); } }); } }); mBaseView = mLoadingAndRetryManager.mLoadingAndRetryLayout;
4.Fragment与ViewPager使用时的注意事项
1.懒加载
由于viewpager会预先缓存几页Fragment,所以Fragment的生命周期在ViewPager中其实是没有多少意义的,因此为了实现用户滑动到这个界面才显示这个界面的数据的功能,我们必须在setUserVisibleHint中加载数据,就像这样
@Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser && !haveInint) { haveInint = true; mPresenter.LoadFirstPage(true); } }
View的缓存
在viewpager的滑动过程中,某个fragment的onCreateView()方法和onDestroyView()方法会多次调用,也就是会先销毁view而保留Fragment中的成员变量,等内存不足是再销毁Fragment。由于View的创建也是很耗时的操作,所以我选择缓存view,而且为了在数据请求返回的时候view都存在,防止每次都有判断view是否为空,因此我将view的创建放在了onCreate()方法中,且先创建view再请求数据。
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); createView(); bindData(); setListener(); mPresenter = new ListPresenter(this); mLoadingAndRetryManager.showLoading(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ViewParent parent = mBaseView.getParent(); if (parent != null && parent instanceof ViewGroup) { ViewGroup group = (ViewGroup) parent; group.removeView(mBaseView); } return mBaseView; }
5.ListView的FooterView与HeardView的显示与隐藏
我觉得这个技巧还是蛮有用的,于是专门写一下。在很多时候我们将view添加到ListView中作为FootView或者HeardView,想隐藏的时候就调用view.setVisibility(View.GONE),然而结果是view的确不显示了,但是它所占用的空间还在,与调用view.setVisibility(View.INVISIBLE)效果类似,后来我发现在添加footview或headview之前用一层layout进行包裹就可以实现隐藏footerview的效果了,代码如下:
//添加footerview tv_bottom_info = (TextView) View.inflate(getActivity(), R.layout.view_bottom_retry, null); tv_bottom_info.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DensityUtil.dip2px(getActivity(), 45))); //要想footerview实现隐藏效果,必须在其外部包裹一层layout,heardview 同理 LinearLayout footerviewParent = new LinearLayout(getActivity()); footerviewParent.addView(tv_bottom_info); tv_bottom_info.setVisibility(View.GONE); mListView.addFooterView(footerviewParent);
Demo
更多细节请看我的demo,欢迎star哈。
MVPDemo
- 使用MVP打造项目框架
- Android 项目框架 使用MVP开发
- android项目框架MVP
- MVP框架 简单使用
- mvp+rxjava2+retrofit2项目框架
- RXjava+Retrofit+dagger2打造自己的MVP框架
- mvp框架的简单使用
- MVP项目框架搭建-高级设计
- MVP 项目框架搭建 — 高级设计
- Android用NoHttp+MVP构建项目框架
- 框架模式MVP在Android中的使用
- 框架模式MVP在Android中的使用
- 框架模式MVP在Android中的使用
- Android中MVP框架的使用
- Android框架模式之-MVP简单使用
- 框架模式MVP在Android中的使用
- 【Android - 框架】之MVP模式的使用
- 优雅的使用MVP+RxJava+Retrofit框架
- java中去除空格或者空白字符
- OpenOffice 将word文档转为pdf再转为图片
- 第一次使用Android Studio时你应该知道的一切配置
- java的三层架构03_持久层(数据处理)
- Java位操作总结
- 使用MVP打造项目框架
- 数据库对象
- QT多线程中,对象信号与槽连接不上的问题
- jpg/png格式图片转eps格式的方法总结
- POJ1861 kruskal.
- 基于OpenCV的面部识别
- jsp/servlet的重定向技术综述
- PHP 程序员的技术成长规划
- file_get_contents("php://input")的使用方法