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的布局就能實現。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM