RecyclerView 下拉刷新上拉加载详解编程语言

步骤:

  1. 首先直接定义一个XRecyclerView继承RecyclerView,重写他的三个构造方法。
  2. init(Context mContext)方法用来初始化底部加载的view
  3. 回到XRecyclerView,实现init
  4. 判断是否滑动到底部,并且进行加载
  5. 自定义一个adapter来把底部布局加进去。
  6. 重写Adapter,通过状态判断是否显示“正在加载”
  7. 定义一个mDataObserver

    1. 首先直接定义一个XRecyclerView继承RecyclerView,重写他的三个构造方法。

public XRecylcerView(Context context) {         
    this(context, null);     
}     
public XRecylcerView(Context context, AttributeSet attrs) {
  this(context, attrs, 0); } public XRecylcerView(Context context, AttributeSet attrs, int defStyle){ super(context, attrs, defStyle); init(context); }

    2. init(Context mContext)方法用来初始化底部加载的view

      先自定义一个底部布局LoadingMoreFooter继承Linearlayout,里面是一个居中显示的ProgressBar和一个TextView,添加一个方法setState(int state),来判定当前刷新的状态

public void setState(int state) { 
        switch (state) { 
            // 刷新中             
      case STATE_LAODING:                
         progressCon.setVisibility(View.VISIBLE);          mText.setText(
"正在刷新");          this.setVisibility(View.VISIBLE);   break;       // 刷新完成       case STATE_COMPLETE:          mText.setText("刷新完成");          this.setVisibility(View.GONE);         break;

      // 没有更多数据       case STATE_NOMORE:           mText.setText("没有更多数据啦");
progressCon.setVisibility(View.GONE);
this.setVisibility(View.VISIBLE); break;     } }

    3. 回到XRecyclerView,实现init

  private void init(Context context) { 
        mContext = context; 
        // loadingMoreEnabled为下拉的开关 
        if (loadingMoreEnabled) { 
            LoadingMoreFooter footerView = new LoadingMoreFooter(mContext); 
            addFootView(footerView); 
            mFootViews.get(0).setVisibility(GONE); 
        } 
    }

      RecyclerView的上拉加载,原理很简单,无非就是当滑动到底部的时候,如果有数据,并且允许加载,就请求数据添加到adapter,而RecyclerView需要做的就是当加载的时候,在底部显示正在加载来提醒用户。  

    4.判断是否滑动到底部,并且进行加载

 1 /** 
 2      * 监听滑动,来定位当前滑动到哪个地方 
 3      * 
 4      * @param state 
 5      */ 
 6     @Override 
 7     public void onScrollStateChanged(int state) { 
 8         super.onScrollStateChanged(state); 
 9         if (state == RecyclerView.SCROLL_STATE_IDLE 
10                 && mLoadingListener != null && !isLoadingData && loadingMoreEnabled) { 
11             LayoutManager layoutManager = getLayoutManager(); 
12             int lastVisibleItemPosition; 
13             if (layoutManager instanceof GridLayoutManager) { 
14                 lastVisibleItemPosition = 
15                         ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); 
16             } else if (layoutManager instanceof StaggeredGridLayoutManager) { 
17                 int[] into = 
18                         new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()]; 
19                 ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into); 
20                 lastVisibleItemPosition = findMax(into); 
21             } else { 
22                 lastVisibleItemPosition = 
23                         ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); 
24             } 
25             if (layoutManager.getChildCount() > 0 
26                     && lastVisibleItemPosition >= layoutManager.getItemCount() - 1 
27                     && layoutManager.getItemCount() > layoutManager.getChildCount() 
28                     && !isnomore) { 
29                 View footView = mFootViews.get(0); 
30                 isLoadingData = true; 
31                 if (footView instanceof LoadingMoreFooter) { 
32                     ((LoadingMoreFooter) footView).setState( 
33                             LoadingMoreFooter.STATE_LAODING); 
34                 } else { 
35                     footView.setVisibility(View.VISIBLE); 
36                 } 
37                 mLoadingListener.onLoadMore(); 
38                 // 一个回调接口,用来加载数据     
39             } 
40         } 
41     }

      写到这个地方,基本的上拉加载的逻辑就搞定了,然后就是处理细节。我们需要把底部布局LoadingMoreFooter加载到RecyclerView,这时候重写setAdapter(Adapter adapter)方法来添加一个adapter。

    5. 自定义一个adapter来把底部布局加进去。自定义的Adapter如下:

  1   private class WrapAdapter extends RecyclerView.Adapter<ViewHolder> { 
  2     private RecyclerView.Adapter adapter; 
  3     private ArrayList<View> mFootViews; 
  4     private int headerPosition = 0; 
  5  
  6     public WrapAdapter(ArrayList<View> footViews, RecyclerView.Adapter adapter) { 
  7         this.adapter = adapter; 
  8         this.mFootViews = footViews; 
  9     } 
 10  
 11     @Override 
 12     public void onAttachedToRecyclerView(RecyclerView recyclerView) { 
 13         super.onAttachedToRecyclerView(recyclerView); 
 14         RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); 
 15         if (manager instanceof GridLayoutManager) { 
 16             final GridLayoutManager gridManager = ((GridLayoutManager) manager); 
 17             gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 
 18                 @Override 
 19                 public int getSpanSize(int position) { 
 20                     return (isFooter(position)) ? gridManager.getSpanCount() : 1; 
 21                 } 
 22             }); 
 23         } 
 24     } 
 25  
 26     @Override 
 27     public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { 
 28         super.onViewAttachedToWindow(holder); 
 29         ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); 
 30         if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams && (isFooter(holder.getLayoutPosition())) 
 31         { 
 32             StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp; 
 33             p.setFullSpan(true); 
 34         } 
 35     } 
 36  
 37     public boolean isFooter(int position) { 
 38         return position < getItemCount() && position >= getItemCount() - mFootViews.size(); 
 39     } 
 40  
 41     public int getFootersCount() { 
 42         return mFootViews.size(); 
 43     } 
 44  
 45     @Override 
 46     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
 47         if (viewType == TYPE_FOOTER) { 
 48             return new SimpleViewHolder(mFootViews.get(0)); 
 49         } 
 50         return adapter.onCreateViewHolder(parent, viewType); 
 51     } 
 52  
 53     @Override 
 54     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
 55         if (isHeader(position)) { 
 56             return; 
 57         } 
 58         int adjPosition = position; 
 59         int adapterCount; 
 60         if (adapter != null) { 
 61             adapterCount = adapter.getItemCount(); 
 62             if (adjPosition < adapterCount) { 
 63                 adapter.onBindViewHolder(holder, adjPosition); 
 64                 return; 
 65             } 
 66         } 
 67     } 
 68  
 69     @Override 
 70     public int getItemCount() { 
 71         if (adapter != null) { 
 72             return getFootersCount() + adapter.getItemCount(); 
 73         } else { 
 74             return getFootersCount(); 
 75         } 
 76     } 
 77  
 78     @Override 
 79     public int getItemViewType(int position) { 
 80         if (isFooter(position)) { 
 81             return TYPE_FOOTER; 
 82         } 
 83         int adjPosition = position; 
 84         int adapterCount; 
 85         if (adapter != null) { 
 86             adapterCount = adapter.getItemCount(); 
 87             if (adjPosition < adapterCount) { 
 88                 return adapter.getItemViewType(adjPosition); 
 89             } 
 90         } 
 91         return TYPE_NORMAL; 
 92     } 
 93  
 94     @Override 
 95     public long getItemId(int position) { 
 96         if (adapter != null) { 
 97             int adjPosition = position - getHeadersCount(); 
 98             int adapterCount = adapter.getItemCount(); 
 99             if (adjPosition < adapterCount) { 
100                 return adapter.getItemId(adjPosition); 
101             } 
102         } 
103         return -1; 
104     } 
105  
106     private class SimpleViewHolder extends RecyclerView.ViewHolder { 
107         public SimpleViewHolder(View itemView) { 
108             super(itemView); 
109         } 
110     } 
111 }

        就是一个继承自RecyclerView.Adapter的adapter,主要用于根据类型加载不同的布局,普通的itemView和“正在加载”的底部提示。 定义一个mDataObserver

     6. 回到setAdapter(Adapter adapter)方法

   /** 
* 重写Adapter,通过状态判断是否显示“正在加载” 
* 
* @param adapter 
*/ 
@Override 
public void setAdapter(Adapter adapter) { 
this.mAdapter = adapter; 
this.mWrapAdapter = new WrapAdapter(mFootViews, mAdapter);// 定义WrapAdapter         
super.setAdapter(mWrapAdapter);// 通过父类方法将自定义的Adapter重新设置进去     
mAdapter.registerAdapterDataObserver(mDataObserver);//请看下面分析    
}

    查看super.setAdapter()方法,会找到adapter.registerAdapterDataObserver(mObserver)方法,当adapter里面的数据发生改变时会即时监听并且更新。

    那为什么还要把mAdapter再设置一遍呢?其实当我们调用通过我们自定义的RecyclerView来调用setAdapter方法时,只有当WrapAdapter数据改变的时候,才会有更新,而当我们仅仅只更新mAdapter里面的数据的时候,如果不监听,我们看到的itemView并没有改变。

    7.定义一个mDataObserver

private final RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() { 
@Override 
public void onChanged() { 
mWrapAdapter.notifyDataSetChanged(); 
} 
@Override 
public void onItemRangeInserted(int positionStart, int itemCount) { 
mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount); 
} 
@Override 
public void onItemRangeChanged(int positionStart, int itemCount) { 
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount); 
} 
@Override 
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { 
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload); 
} 
@Override 
public void onItemRangeRemoved(int positionStart, int itemCount) { 
mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount); 
} 
@Override 
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 
mWrapAdapter.notifyItemMoved(fromPosition, toPosition); 
} 
};

    都是直接调用父类的方法就可以。

  这样一个RecyclerView的上拉加载逻辑就全部搞定了,这是极其简单的封装方法,所以逻辑没有多么的复杂。

  好的,来回顾一下逻辑:重写RecyclerView进行滑动监听,当滑动到底部的时候通过重写setAdapter来将底部视图加载出来,最后对mAdapter进行数据更改的监听。

  上拉刷新的逻辑更简单,因为有谷歌的SwipeRefreshLayout,所以实现起来就简单很多,首先来看布局文件

<android.support.v4.widget.SwipeRefreshLayout 
  android:id="@+id/swipe" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content"/> 
<com.baiyyyhjl.pullrecyclerview.recyclerview.XRecylcerView 
android:id="@+id/recyclerview" 
android:layout_width="match_parent" 
android:layout_height="wrap_content"/> 
</android.support.v4.widget.SwipeRefreshLayout>

  直接用SwipeRefreshLayout将我们刚才自定义的RecyclerView包裹起来,然后swipeRefreshLayout.setOnRefreshListener(this)进行监听,实现onRefresh()接口来实现加载的逻辑。

  就这样,一个简单实用的RecyclerView上拉加载,下拉刷新就实现了。没有多余的布局文件,极其简便。

  当我们项目中有多个RecyclerView并且要求上拉加载,下拉刷新的时候,我们可以定义一个抽象类,只通过修改itemView的布局就能实现。

  

 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13425.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论