不用继承!使用组合的方式实现下拉刷新和上拉加载

栏目: Android · 发布时间: 8年前

内容简介:不用继承!使用组合的方式实现下拉刷新和上拉加载

为什么要使用组合的方式

Android下拉刷新和上拉加载的框架网上已经非常多了,但是大多数都需要继承自定义的下拉SwiperefreshLayout,或者自定义recyclerview, 或者自定的adapter。主要有以下几种:

  • 必须要继承自定义的xxRefreshLayout。 将下拉刷新控件及listview整个封装到一个控件里。

  • 必须继承自定义的xxListView。将上拉加载功能封装到自定义的listview或者RecyclerView里。

  • 必须继承作者自定义的Adapter。

无论哪种我都不愿意使用。我可能有自己的下拉控件,也可能直接使用原生的的SwipeRefreshLayout(会有一些定制修改)。列表部分虽然大部分情况下使用RecyclerView,但是有的地方也用到了ListView。至于Adapter,我更是不会去继承了。相信大家都有自己的BaseAdapter,虽然大同小异,但是也有独特和不同的地方,为了LoadMore去继承修改自己的BaseAdapter很糟糕。 比如我的BaseAdapter封装了click事件,并基于DataBinding,连ViewHolder都省了。

从代码设计上考虑,继承并不是很好的选择,代码入侵性太强。还是优先使用基础控件,除非是Google等知名第三方库。这里我更倾向于使用组合包装的方式实现LoadMoreHelper。下拉控件可能是PtrFrameLayout也可能是SwiperefreshLayout, 列表控件可能是ListView 也可能是RecyclerView,使用自己的BaseAdapater即可。LoadMoreHelper并不是自定义控件,而是通过数据Loader,对各个组件进行设置。而对于各个组件来说不需要依赖LoadMoreHelper。

不用继承!使用组合的方式实现下拉刷新和上拉加载

load_more.png

下拉刷新与上拉加载过程分析

虽然两者看起来很相似,但是还是有区别。一些控件将两种行为强行整合到一起是不恰当的。所以一般下拉刷新控件都不会提供上拉加载功能,需要用户自己去实现

下拉刷新和上拉加载是两种不同的控件功能,不要混淆在一起。

目前最好用的是Google的SwipeRefreshLayout,有的工程还用到了仿ios的PtrFrameLayout,尽管该控件不再维护更新了,但是是否使用是取决于开发者,LoadMoreHelper不做限制。下拉样式及处理滑动事件都是由下拉控件负责定制和处理,LoadMoreHelper不用关心这些。

先看一下数据加载的大致流程:

不用继承!使用组合的方式实现下拉刷新和上拉加载

Pull_and_loadmore.png

虽然下拉刷新和上拉加载在UI体验上不同,但是其装载数据的过程却差不多

下拉刷新load第1页数据,并替换当前全部list数据

上拉加载load大于第1页的数据,并将加载的数据补充到当前list里

调用者必须实现数据加载接口,获取数据结果,并通过数据装载器通知UI变化。

  • 数据加载接口

可以是同步,可以是异步。

同步数据加载接口SyncDataLoader直接返回数据结果,LoadMoreHelper会在后台线程调用该接口;

异步数据加载接口AsyncDataLoader,在数据加载成功后的回调用LoadMoreHelper.onLoadEnd,通知数据结果

  /**
   * Load data sync. LoadHelper will call it on work thread
   */
      @WorkerThread
  public interface SyncDataLoader

  
   
  {
          PageData
 
   
    
   startLoadData(int page, PageData
  
    
     
    lastPageData);
  }

  /**
   * Load data int async thread
   */
      public interface AsyncDataLoader
   
     
      
     {
          void startLoadData(int page, PageData
    
      
       
      lastPageData);
      }
    
      
   
     
  
    
 
   

  
  • 数据结果PageData的定义

主要包含,1.pageIndex,当前页码,2. list,数据,3. pageMore,是否还有下一页数据,4. result,是否加载成功

  public final class PageData
{       private int pageIndex;          private List

data;          private boolean pageMore;          private boolean success = true;      }

  • 数据装载接口

数据装载的接口定义,IDataSwapper,在下拉刷新时调用swapData, 在上拉加载时调用appendData。建议使用Adapter继承该接口,当然也可以单独实现。

  public interface IDataSwapper

   
    
  {
      /**
       * Swap all datas
       */
      void swapData(List extends VM> list);
      /**
       * Append data to the end of current list
       */
      void appendData(List extends VM> list);
  }

   

使用装饰模式添加加载更多view

由于RecyclerView不能像ListView那样直接添加headerview或者footerview,需要自己在Adapter里实现。而这里我们又并不想继承该Adapter,使用装饰模式将调用者的Adapter包装起来。这样对调用者来说,只需要关注自己的Adapter即可,不需要关注包装者。

我们先看一下Android原生的ListView是如何实现header和footer

在添加footer后,会将原adapter包装成HeaderViewListAdapter

    public void addFooterView(View v, Object data, boolean isSelectable) {
    ...
    // Wrap the adapter if it wasn't already wrapped.
    if (mAdapter != null) {
        if (!(mAdapter instanceof HeaderViewListAdapter)) {
            wrapHeaderListAdapterInternal();
        }
        ...
    }
}

public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
private final ListAdapter mAdapter;
...
}

参考该设计,类似的我们在添加RecyclerView的footerView时,也可以包装一个FooterAdapter

//包装origin adapter
RecyclerView.Adapter

   
    
  originAdapter = recyclerView.getAdapter();
footerAdapter = new FooterViewAdapter<>(originAdapter);
        recyclerView.setAdapter(footerAdapter);

   

FooterAdapter 会将原来的Adapter包装起来,并在尾部添加 加载更多的view

    //FooterAdapter 继承于BaseWrapperAdapter
       public class FooterViewAdapter

   
    
  extends BaseWrapperAdapter

    public class BaseWrapperAdapter
 
    
     
  
    extends RecyclerView.Adapter
  
     
      
    {
        private RecyclerView.Adapter
   
      
       
     mWrappedAdapter;
     ...
     }
   
      
  
     
 
    

   

如此,便可以不要求用户继承LoadMoreHelper的Adapter,又能在RecyclerView的末尾添加footerView。对于调用者来说,这一层完全是透明的,只需要按照之前的习惯调用原Adapter的notifyDataSetChanged即可,不需要关注包装者的存在。

LoadMoreHelper调用形式

布局文件使用原生的控件即可。其中SwipeRefreshLayout可替换成PtrFrameLayout,RecyclerView可替换成ListView.

Api采用链式调用。推荐使用lambda表达式使代码更简洁。

  • 首先设置swipeRefreshLayout,会自动需要包含的RecyclerView。 如果不需要下拉刷新只用上拉加载的话,可以直接设置RecyclerView。在LoadMoreHelper内部,会根据传入的view进行设置,设置RecyclerView的滑动监听,包装Adapter等。

     loadHelper = LoadMoreHelper.create(swipeRefreshLayout)
         .setDataSwapper(adapter)
         .setAsyncDataLoader((page, lastPageData) -> doLoadData(page))
         .startPullData(true);
  • 设置IDataSwapper。这里Adapter实现了IDataSwapper接口。当然可以不使用Adapter继承而单独实现该接口,具体可参见工程里的例子。

private class MyAdapter2 extends RecyclerView.Adapter implements IDataSwapper {

...

 @Override
 public void swapData(List extends Item> list) {
     datas.clear();
     datas.addAll(list);
     notifyDataSetChanged();
 }
5
 @Override
 public void appendData(List extends Item> list) {
     if (list == null || list.isEmpty()) {
         return;
     }
     int start = datas.size();
     datas.addAll(list);
     notifyItemRangeInserted(start, list.size());
 }

}

  • 设置AsyncDataLoader。 采用异步数据加载,这里的loadData使用了Rxjava做例子。LoadMoreHelper会在下拉刷新或者上拉加载时调用 startLoadData。通知当前所需数据的页码,调用者只需填充加载方法,并将数据加载结果通过onLoadEnd传入。

      new LoadMoreHelper.AsyncDataLoader

   
    
 (){
     @Override
     public void startLoadData(int page, PageData lastPageData) {
         DataLoader.loadData(pageIndex, null)
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(result -> {
                     PageData
 
    
     
   pageData = PageData.createSuccess(pageIndex, result.getData(), result.isPageMore());
                     loadHelper.onLoadEnd(pageData);
                 }, e -> {
                     PageData
  
     
      
    pageData = PageData.createFailed(pageIndex);
                     loadHelper.onLoadEnd(pageData);
                 });
     }
 };
  
     
 
    

   

LoadMoreHelper也提供了一些其他接口可以设置加载更多viewsetLoadMoreViewCreator,加载失败viewsetLoadFailedViewCreator,加载完毕viewsetLoadCompleteViewCreator。页面数据加载失败后可以点击重试。

代码见:

https://github.com/liyuanhust/LoadMoreHelper


以上所述就是小编给大家介绍的《不用继承!使用组合的方式实现下拉刷新和上拉加载》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hit Refresh

Hit Refresh

Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37

Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具