分页
分页也是APP中必不可少的功能, 一般配合 滚动到底部自动加载的控件 一起使用. 但是光有控件还不够, 我们还需要在Presenter中去控制页数, 什么时候能加载, 什么时候不能加载. 下拉刷新的时候还需要重置页数. 这些代码都是可以复用的, 因此就有了BasePaginationPresenter.
旧的模式
旧的分页模式将分页的通用逻辑都写在BasePaginationPresenter中,如控制页数,判断页数等。一旦分页逻辑有变化就需要改动BasePaginationPresenter,甚至可能影响到Presenter的写法,甚是丑陋。如果想扩展另外一种分页模式,除了改代码别无他法。因此催生的重构分也逻辑的想法。
新的模式
新的分页模式将分页的逻辑抽取出来,并使用策略+工厂模式封装,并将BasePaginationPresenter继续细化为使用不同分页策略的Presenter,这样使得一旦分页逻辑有变,只需要修改策略或者BasePaginationPresenter的子类,而不用去动BasePaginationPresenter。扩展也只需要新建一个策略以及BasePaginationPresenter的子类。
下面来看看写法,这里以页数分页为例-即根据服务器返回的总页数以及当前页数来判断是否能分页:
首先,Presenter需要使用PagePresenter,PagePresenter是BasePaginationPresenter的子类,BasePaginationPresenter中需要的是BasePaginationView类型的view接口,也就是说Activity/Fragment必须实现继承自BasePaginationView的view接口.
BasePaginationView定义:
一般情况下, 我们需要在onLoadingCompleted()中隐藏加载框, 在onAllPageLoaded()中禁止自动加载.
如何使用PagePresenter和BasePaginationView? 首先我们需要这样定义我们的Activity/Fragment以及Presenter和View:
接着按照下面的代码来写Presenter中需要做分页的方法. 如获取热门产品的方法:
关键的地方就在if (!doPagination(reload)) return;
这一句。doPagination方法位于BasePaginationPresenter中,翻页的通用逻辑就定义在该方法中。如果doPagination返回了false, 则意味当前不能再加载数据了, 需要return退出函数。如果为true, 则加载下一页数据。
一般分页接口都需要当前的页数, 以及每页加载多少条数据, 我们只需要使用PagePresenter的getPageNo()与getPageSize()方法即可。当然不同的分页策略可能需要的参数都不一样,我们需要根据具体的策略具体对待。
与普通网络请求不一样,这里需要使用PaginationSubscriber而不是ResponseSubscriber来订阅请求结果。接着请求下一页数据成功之后, 服务器会返回总页数, 这个时候我们需要在PaginationSubscriber的getCondition中返回总页数。并且在getListResult中返回服务器返回的List集合。最后,翻页成功并且返回的数据不为空的情况下,会回调PaginationSubscriber的onDataNotNull方法。如果返回为空,会回调onDataIsNull方法。默认此方法会调用BaseView的showEmptyHint方法,当然也可以选择覆写。
到此翻页的逻辑算是完成了一大部分. 接着看看Activity/Fragment如何去实现BasePaginationView的两个方法:
全部是转发PtrAutoLoadMoreLayout的方法. 我们也可以覆写BaseView的showLoading与hideLoading方法, 覆盖BaseActivity的默认加载框, 而使用PtrAutoLoadMoreLayout的下拉式加载:
接下来, 由于要配合PtrAutoLoadMoreLayout一起实现分页功能, 因此肯定需要实现PtrAutoLoadMoreLayout的RefreshLoadCallback接口:
注意: 由于PtrLollipopLayout/PtrAutoLoadMoreLayout的setRefreshing会回调RefreshCallback/RefreshLoadCallback的onRefreshing方法, 所以在onRefreshing中我们需要判断当前是不是自动刷新的if (!frame.isAutoRefresh())
, 自动刷新就不要请求数据了, 不然就会重复执行两次. 又因为我们覆写了BaseActivity的showLoading方法为ptrAutoLoadMoreLayout.setRefreshing()
, 同样每次showLoading的话就会回调onRefreshing一次, 因此在Presenter中建议加上if (reload) view.showLoading()
.
到此, 整个分页功能就完成了. 可以看到整体还是不是很简单的. BasePaginationPresenter也只是抽象了通用的分页的逻辑,实现则交由具体的分页策略。下面看看关键方法doPagination:
接着来看看分页策略PaginationStrategy接口的定义:
根据页数分页的具体逻辑实现类则是PageStrategy。PageStrategy内部维护了一个Page类,有当前页数,每页个数,总页数等字段。PageStrategy实现了PaginationStrategy,接着来看看canDoPagination,doPagination以及setCondition的实现:
细心的朋友可能会疑惑了, doPagination方法中仅仅回调了onAllPageLoaded方法, 而没有onLoadingCompleted方法. 那么onLoadingCompleted方法是在何处调用的? 下面看看ResponseHandler类的resetLoadingStatus方法(不知道ResponseHandler的同学可以参考ResponseSubscriber,PaginationSubscriber继承自ResponseSubscriber):
到此, 整个分页的逻辑就很清晰了. 分页功能是由分页策略(BasePaginationPresenter,PaginationStrategy), Activity/Fragment(BasePaginationView), PaginationSubscriber以及PtrAutoLoadMoreLayout四方合作的结果.
扩展分页策略
如何扩展分页策略?这里以页书分页的一个变种为例--根据数据集合个数来判断是否能分页,分页逻辑则和页数分页一样。这里我们只需要新建两个类:
ListResultStrategy
ListResultStrategy需要实现PaginationStrategy,但由于其分页逻辑与PageStrategy一致,因此可以直接继承PageStrategy:
ListPagePresenter
由于两种分页需要的条件一样,因此ListPagePresenter类似于PagePresenter
只需要在构造函数里设置策略为ListResultStrategy。
StrategyFactory
前面提到分页使用了策略+工厂模式,StrategyFactory就是一个工厂类,一个非常简单的没有接口的工厂类,并且该工厂是一个枚举。这里为何要多此一举的使用工厂模式,而不是直接new出来具体的策略?主要是因为策略模式的缺陷:具体的策略必须暴露出去,并且还要由上层模块初始化,这不合适,高层次模块对低层次模块应该仅仅处在“接触”的层次上,而不是“耦合”的关系,否则维护的工作量会非常大。正好工厂方法可以帮我们生产指定的对象。
在新建了策略类和Presenter之后,就需要在StrategyFactory添加一个属性
ListResultStrategy("com.corelibs.pagination.strategy.ListResultStrategy")
,策略名加上路径即可。使用不同的策略在写法上的不同只有继承不同的Presenter,获取不同的分页条件,以及getCondition的实现不同。
Last updated
Was this helpful?