Android MVP复杂页面的实现方式

距离上一篇Android MVP从懵逼到入门:登陆业务实践已经有一段时间了,这段时间忙着公司的项目,都没找时间写写文章,今天就把这段时间整理的MVP知识再总结一下,这篇文章主要介绍我是如何使用MVP模式来实现主页多个Tab切换的场景的。

说是复杂,其实只是多了一些Fragment而已,在上一篇文章中,我们说Activity的主要作用是创建view和presenter,并把view实例传入到presenter中,那么,对于多个Tab切换的主页面(现在的大多数app都是这样的主页),Activity的作用就又多了一个,也是大家都知道的:管理Fragment。其实这没什么想不到的,在没说MVP的时候,大家也都是这么做的。

这里我参考网上网友收集的知乎日报的相关api,使用mvp模式简单的实现了知乎日报主页的内容。先看一下效果:

  • Android MVP复杂页面的实现方式

    • presenter
    • view
    • model
    • avtivity
    • 周末愉快

presenter

先看看presenter怎么写,应该说跟上一篇的实现是一样的,那就得先写好契约类–MainContact.java:

public interface MainContract {interface Presenter extends BasePresenter {RootEntity getLatestNews();RootEntity getSafety();RootEntity getInterest();RootEntity getSport();}interface View extends BaseView<Presenter> {void setTitle();void refresh(List<StoriesEntity> list);}
}

写好契约类,那么这个模块有哪些主要功能基本都清楚了,看view接口,可以知道,页面可以实现的功能有:

  • 1.设置页面标题–setTitle
  • 2.刷新页面内容–refresh

看presenter接口,可以知道,model提供了那些数据访问接口:

  • 1.获取今日日报模块内容
  • 2.获取网络安全模块内容
  • 3.获取不许无聊模块内容
  • 4.获取体育日报模块内容

契约类写好后,就可以实现presenter接口了,看看MainPresenter.java类:

public class MainPresenter implements MainContract.Presenter {private String baseUrl = "http://news-at.zhihu.com";private MainContract.View mMainView;private Context mContext;protected ZhiHuService service;public MainPresenter(Context context) {this.mContext = context;}public void setView(MainContract.View view) {this.mMainView = view;mMainView.setPresenter(this);mMainView.setTitle();service = getService();}public ZhiHuService getService() {Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();service = retrofit.create(ZhiHuService.class);return service;}@Overridepublic RootEntity getLatestNews() {return loadData(service.getLatestNews());}@Overridepublic RootEntity getSafety() {return loadData(service.getSafety());}@Overridepublic RootEntity getInterest() {return loadData(service.getInterest());}@Overridepublic RootEntity getSport() {return loadData(service.getSport());}@Overridepublic void start() {}public RootEntity loadData(Observable<RootEntity> observable) {final RootEntity rootEntity = new RootEntity();observable.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).map(new Func1<RootEntity, ArrayList<StoriesEntity>>() {@Overridepublic ArrayList<StoriesEntity> call(RootEntity rootEntity) {return rootEntity.getStories();}}).subscribe(new Subscriber<ArrayList<StoriesEntity>>() {@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onNext(ArrayList<StoriesEntity> storiesEntities) {rootEntity.setStories(storiesEntities);mMainView.refresh(storiesEntities);}});return rootEntity;}
}

view

view其实才是这篇文章的主题,契约类里面定义的一些接口其实是所有的view实现类的集合,比如这里有4个Fragment页面,每个页面展示的内容是不一样的,需要的接口也不一样,这些接口都需要在契约类里面定义,然后每个Fragment都去实现这个接口,完成对应接口中的内容。

比如这4个Fragment都去实现MainContract.View,各自都实现setTitle和refresh内容,如何还有其他需要实现的接口,同样定义在契约类里面,谁关心这个接口,谁就去实现这个接口,view不必关心接口的调用,只需要实现内容就行了,这在MVP中体现的比较明显:面向接口编程。(我所接触的面向接口编程的另一种场景就是模块化编程,感觉有些类似。)

既然有4个Fragment,那么他们肯定有共同之处,至少他们都是Fragment,何不提取基类呢?这相比大家都能想到–Java语言的三大特性即:封装、继承、多态

来看看BaseFragment的内容:

public class BaseFragment extends Fragment implements MainContract.View {@BindView(R.id.lv_news)ListView mListView;protected MainContract.Presenter mPresenter;protected ActionBar mActionBar;private ZhiHuNewsAdapter mAdapter;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,@Nullable Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_main_0, container, false);ButterKnife.bind(this, view);return view;}@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {mAdapter = new ZhiHuNewsAdapter(getContext());mListView.setAdapter(mAdapter);}@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);if (isAdded()) {mActionBar = ((AppCompatActivity)getActivity()).getSupportActionBar();setTitle();}}@Overridepublic void setPresenter(MainContract.Presenter presenter) {this.mPresenter = presenter;}@Overridepublic void refresh(final List<StoriesEntity> list) {mAdapter.setNewsList(list);mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {Intent intent = new Intent(getActivity(), StoryDetailActivity.class);intent.putExtra(StoryDetailFragment.STORY_ID, list.get(position).getId());intent.putExtra(StoryDetailFragment.STORY_TITLE, list.get(position).getTitle());startActivity(intent);}});}@Overridepublic void setTitle() {if (mActionBar != null) {mActionBar.setTitle(R.string.app_name);}}
}

model

因为这里没有考虑到数据的持久化,没有创建本地数据库,所以,model的实现其实就是一个service,因为是用reftrofit2来完成网络请求的,所以,model就是ZhiHuService.java:

public interface ZhiHuService {//今日头条@GET("/api/4/news/latest")Observable<RootEntity> getLatestNews();//互联网安全@GET("/api/4/theme/10")Observable<RootEntity> getSafety();//不准无聊@GET("/api/4/theme/11")Observable<RootEntity> getInterest();//体育日报@GET("/api/4/theme/8")Observable<RootEntity> getSport();
}

avtivity

最后看看activity,作用有三个:创建view(这里是views)、创建presenter、管理fragemnt:

public class MainActivity extends AppCompatActivity {@BindView(R.id.tab_item_main_0)TabItem tabItemMain0;@BindView(R.id.tab_item_main_1)TabItem tabItemMain1;@BindView(R.id.tab_item_main_2)TabItem tabItemMain2;@BindView(R.id.tab_item_main_3)TabItem tabItemMain3;@BindView(R.id.tab_item_main_4)TabItem tabItemMain4;@BindView(R.id.toolbar)Toolbar toolbar;@BindView(R.id.fab)FloatingActionButton floatingBar;private FragmentManager mFragmentManager;private TodayFragment mTodayFragment;private InterestFragment mInterestFragment;private SafetyFragment mSafetyFragment;private SportFragment mSportFragment;private OtherFragment mOtherFragment;private MainPresenter mPresenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);mFragmentManager = getSupportFragmentManager();setSupportActionBar(toolbar);mPresenter = new MainPresenter(getApplicationContext());tabItemMain0.performClick();}public void showFragment(int tag) {if (mFragmentManager != null) {FragmentTransaction transaction = mFragmentManager.beginTransaction();hideFragments();switch (tag) {case TagStatic.TAG_FRAGMENT_TODAY:mTodayFragment = (TodayFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_TODAY + "");if (mTodayFragment == null) {mTodayFragment = new TodayFragment();transaction.add(R.id.fragment_content, mTodayFragment, tag + "");} else {transaction.show(mTodayFragment);}mPresenter.setView(mTodayFragment);break;case TagStatic.TAG_FRAGMENT_INTEREST:mInterestFragment = (InterestFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_INTEREST + "");if (mInterestFragment == null) {mInterestFragment = new InterestFragment();transaction.add(R.id.fragment_content, mInterestFragment, tag + "");} else {transaction.show(mInterestFragment);}mPresenter.setView(mInterestFragment);break;case TagStatic.TAG_FRAGMENT_SAFETY:mSafetyFragment = (SafetyFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_SAFETY + "");if (mSafetyFragment == null) {mSafetyFragment = new SafetyFragment();transaction.add(R.id.fragment_content, mSafetyFragment, tag + "");} else {transaction.show(mSafetyFragment);}mPresenter.setView(mSafetyFragment);break;case TagStatic.TAG_FRAGMENT_SPORT:mSportFragment = (SportFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_SPORT + "");if (mSportFragment == null) {mSportFragment = new SportFragment();transaction.add(R.id.fragment_content, mSportFragment, tag + "");} else {transaction.show(mSportFragment);}mPresenter.setView(mSportFragment);break;case TagStatic.TAG_FRAGMENT_OTHER:mOtherFragment = (OtherFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_OTHER + "");if (mOtherFragment == null) {mOtherFragment = new OtherFragment();transaction.add(R.id.fragment_content, mOtherFragment, tag + "");} else {transaction.show(mOtherFragment);}mPresenter.setView(mOtherFragment);break;}transaction.commitAllowingStateLoss();}}private void hideFragments() {FragmentTransaction transaction = mFragmentManager.beginTransaction();mTodayFragment = (TodayFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_TODAY + "");if (mTodayFragment != null) {transaction.hide(mTodayFragment);}mInterestFragment = (InterestFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_INTEREST + "");if (mInterestFragment != null) {transaction.hide(mInterestFragment);}mSafetyFragment = (SafetyFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_SAFETY + "");if (mSafetyFragment != null) {transaction.hide(mSafetyFragment);}mSportFragment = (SportFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_SPORT + "");if (mSportFragment != null) {transaction.hide(mSportFragment);}mOtherFragment = (OtherFragment) mFragmentManager.findFragmentByTag(TagStatic.TAG_FRAGMENT_OTHER + "");if (mOtherFragment != null) {transaction.hide(mOtherFragment);}transaction.commitAllowingStateLoss();}@OnClick({R.id.tab_item_main_0, R.id.tab_item_main_1, R.id.tab_item_main_2,R.id.tab_item_main_3, R.id.tab_item_main_4, R.id.fab})public void onClick(View view) {clearChecked();switch (view.getId()) {case R.id.tab_item_main_0:tabItemMain0.setChecked(true);showFragment(TagStatic.TAG_FRAGMENT_TODAY);break;case R.id.tab_item_main_1:tabItemMain1.setChecked(true);showFragment(TagStatic.TAG_FRAGMENT_INTEREST);break;case R.id.tab_item_main_2:tabItemMain2.setChecked(true);showFragment(TagStatic.TAG_FRAGMENT_SAFETY);break;case R.id.tab_item_main_3:tabItemMain3.setChecked(true);showFragment(TagStatic.TAG_FRAGMENT_SPORT);break;case R.id.tab_item_main_4:tabItemMain4.setChecked(true);showFragment(TagStatic.TAG_FRAGMENT_OTHER);break;case R.id.fab:Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_SHORT).setAction("Action", null).show();break;}}private void clearChecked() {tabItemMain0.setChecked(false);tabItemMain1.setChecked(false);tabItemMain2.setChecked(false);tabItemMain3.setChecked(false);tabItemMain4.setChecked(false);}
}

avtivity里的内容还是比较少的,只是多了Fragment的显示和隐藏,没有变得复杂和职责不清,对于管理和维护也很简单。因为有多个view实现类,所以,我没有在presenter的初始化时传入view实例,而是通过在presenter中的setView方法设置不同的view。

跟retrofit2相关的内容这里不展开说,不知道的、想了解的可以自行搜索,这里是GitHub地址:retrofit,以及本文的项目源码:Login-MVP-Architecture ,希望对MVP感兴趣以及任何想要一起提高Android开发技能的小伙伴能star一下,以后会继续在这个项目上添加新的内容,包括一些有用的工具类、各种控件、效果实现、设计模式的使用等等。

周末愉快(●’◡’●)

Android MVP进阶:“修行在个人”相关推荐

  1. Android MVP 详解(上)

    作者:李旺成 时间:2016年4月3日 "Android MVP 详解(下)"已经发布,欢迎大家提建议. MVP 在 Android 上的使用其实已经有挺长一段时间了,长到似乎有点 ...

  2. [Android] Android MVP 架构下 最简单的 代码实现

    Android  MVP 架构下  最简单的 代码实现 首先看图: 上图是MVP,下图是MVC MVP和MVC的区别,在于以前的View层不仅要和model层交互,还要和controller层交互.而 ...

  3. Android开发进阶之NIO非阻塞包(一)

    Android开发进阶之NIO非阻塞包 这个系列转载于http://www.android123.com.cn/androidkaifa/695.html,特此说明 对于Android的网络通讯性能的 ...

  4. Android MVP模式 简单易懂的介绍方式

    主要学习这位大神的博客:简而易懂 Android MVP模式 简单易懂的介绍方式 https://segmentfault.com/a/1190000003927200 转载于:https://www ...

  5. Android MVP模式简单易懂的介绍方式 (一)

    Android MVP模式简单易懂的介绍方式 (一) Android MVP模式简单易懂的介绍方式 (二) Android MVP模式简单易懂的介绍方式 (三) 最近正在研究Android的MVP模式 ...

  6. Android日志[进阶篇]五-阅读错误报告

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  7. Android日志[进阶篇]四-获取错误报告

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  8. Android日志[进阶篇]三-Logcat 命令行工具

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  9. Android日志[进阶篇]一-使用 Logcat 写入和查看日志

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

最新文章

  1. Chrome Extension 检查视图(无效)处理方法
  2. python心得体会300字_有没有简单一点的 Python 小例子或小项目?
  3. JavaScript实现sieveOfEratosthenes埃拉托色尼筛选法算法(附完整源码)
  4. python实现决策树算法sklearn_python sklearn-05:决策树及随机森林
  5. C++小型公司管理系统
  6. mysql没多久自动断开服务_mysql 长时间没连接了 就会自动断开服务
  7. mysql备份到邮箱,备份网站mysql数据到邮箱/ftp
  8. TF下载量已超4600万!首届TensorFlow World大会,谷歌大牛Jeff Dean激情演讲
  9. log4j的日志级别
  10. LucasExlucas
  11. 数组的存储与初始化、对象数组、数组作为函数参数
  12. JavaScript中的输入输出语句
  13. [渝粤教育] 中国地质大学 大学英语(4) 复习题
  14. 拓端tecdat|R语言作图不显示中文解决办法,如何使用中文字体
  15. linux 设置tomcat快捷启动方式
  16. 一种通感一体化的信号设计与性能分析
  17. CAS配置REST请求方式
  18. 精度LiDAR-Monocular Visual Odometry
  19. 第七届蛋白质与蛋白质组学国际研讨会(CPP 2022)
  20. Windows7 VS2015 下编译 PythonQt3.2

热门文章

  1. 20190614—求平均数,并将低于平均数的数值列出来
  2. XPSPEAK41软件及教程
  3. 最全HTML笔记总结二
  4. 巧用这几款多特瑞产品,感受别样冬日暖
  5. Linux 防范病毒的方法
  6. springBoot CORS跨域注解@CrossOrigin
  7. 洛谷水题实况(P4971 断罪者)
  8. 洛谷P4971:断罪者(左偏树)
  9. 远程管理 KVM 虚机 - 每天5分钟玩转 OpenStack(5)
  10. Android口袋天气系统一--整体架构