Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 輕松實現RecycleView的下拉刷新、加載更多

輕松實現RecycleView的下拉刷新、加載更多

編輯:關於Android編程

PullRefresh.gif

那如同這個題目,這裡面涉及的東西其實還是比較多的,RecycleView SwipeRefreshLayout,下拉刷新(這個就是SwipeRefreshLayout的),加載更多。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="swiperefreshlayout">SwipeRefreshLayout

這個是Google自己封裝的一個下拉刷新的控件,裡面使用了5.0開始的嵌套滑動機制,有興趣的朋友可以去看看源碼!使用起來其實就涉及到以下方法:

setOnRefreshListener() 下拉刷新的相關回調。

setRefresh() 通知是否開始刷新或者刷新完成。(坑1)

setColorSchemeColors() loading的時候progressbar的顏色,支持多個。

SwipeRefreshLayout的坑

進入頁面調用setRefresh(true),更不不顯示刷新的小圓圈?!
簡單的說,這個就是在oncreate()方法執行的時候,view還沒有繪制出來,這個時候你設置刷新不刷新其實都一樣的,解決方法,post一下!

mRecyclerView.post(new Runnable() {
        @Override
        public void run() {
            mRefreshLayout.setRefreshing(refresh);
        }
    });

RecycleView

RecycleView其實出現都有一定的年頭了,前幾天公司來面試的居然說他還沒有用過。。這個也是醉醉的!

RecycleView是ListView的強力升級!加入了holder便於管理和復用相同的類型。

就我目前掌握的情況,RecycleView對於ListView有了以下的不同:

1、加入了LayoutManager用用管理各種類型的布局,而且通過不同的布局可以實現橫向、豎向、瀑布式的等各種復雜的布局。

2、加入Holder來管理相關布局和復用,對於每一種Type的View你都要創建一個對應的Holder來管理它!

3、取消了header和bottom布局。

4、沒有現成的itemClick回調。

5、引入了豐富的動畫效果。(坑4)

6、添加了豐富的數據刷新的方法,可以局部刷新了!(坑3)

7、可自定義相關分割線。

8、支持swipe刪除和drag排序。(ItemTouchHelper 幫助類)

9、默認是不顯示scrollBar的(坑2)

10、可以設置不同類型holder占據不同的空間(ItemColumnSpan)

上面這些不是所有的都講,其實本文主要涉及的就是相關adapter,裡面對應不同的holder,及相關的封裝。然後說說踩了哪些坑。

基本思路

1、明確什麼時候開始加載更多?

下拉刷新就調用SwipeRefreshLayout相關就好了,那麼加載更多呢?這個就要自己去寫相關的布局了。然後第一個問題,什麼時候加載更多??因為RecycleView有各種布局,所以判斷最後一個也是要區分不同的adapter的!

2、加載更多有多少種情況?
大致有三種,正在加載更多;加載更多錯誤;沒有更多數據了;

具體實現

1、監聽滑動,滿足條件開始加載更多。

        @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (null != scrollListener) {
            scrollListener.onScrolled(SwipeRefreshRecycleView.this, dx, dy);
        }
        if (null == manager) {
            throw new RuntimeException("you should call setLayoutManager() first!!");
        }
        if (null == adapter) {
            throw new RuntimeException("you should call setAdapter() first!!");
        }
        if (manager instanceof LinearLayoutManager) {
            int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) manager).findLastCompletelyVisibleItemPosition();

            if (adapter.getItemCount() > 1 && lastCompletelyVisibleItemPosition >= adapter.getItemCount() - 1 && adapter.isHasMore()) {
                adapter.isLoadingMore();
                if (null != listener) {
                    listener.onLoadMore();
                }
            }
            int position = ((LinearLayoutManager) manager).findFirstVisibleItemPosition();
            if (lastTitlePos == position) {
                return;
            }
            lastTitlePos = position;
        }
        if (manager instanceof StaggeredGridLayoutManager) {
            int[] itemPositions = new int[2];
            ((StaggeredGridLayoutManager) manager).findLastVisibleItemPositions(itemPositions);

            int lastVisibleItemPosition = (itemPositions[1] != 0) ? ++itemPositions[1] : ++itemPositions[0];

            if (lastVisibleItemPosition >= adapter.getItemCount()  && adapter.isHasMore()) {
                adapter.isLoadingMore();
                if (null != listener) {
                    listener.onLoadMore();
                }
            }

        }

    }

2、定義自己的加載更多的ViewHolder。

3.定義相關的方法實時更新ViewHolder的三種狀態。

public class NewBottomViewHolder extends RecyclerView.ViewHolder{
    @Bind(R.id.footer_container)
    public LinearLayout contaier;

    @Bind(R.id.progressbar)
    ProgressBar pb;
    @Bind(R.id.content)
    TextView content;
    @Nullable
    private final SwipeRefreshRecycleView.OnRefreshLoadMoreListener mListener;

    public NewBottomViewHolder(View itemView, SwipeRefreshRecycleView.OnRefreshLoadMoreListener listener) {

        super(itemView);
        ButterKnife.bind(this,itemView);
        mListener = listener;
    }

    public void bindDateView(int state) {
        switch (state) {
            case AdapterLoader.STATE_LASTED:
                contaier.setVisibility(View.VISIBLE);
                contaier.setOnClickListener(null);
                pb.setVisibility(View.GONE);
                content.setText("---  沒有更多了  ---");
                break;
            case AdapterLoader.STATE_LOADING:
                contaier.setVisibility(View.VISIBLE);
                content.setText("加載更多!!");
                contaier.setOnClickListener(null);
                pb.setVisibility(View.VISIBLE);
                break;
            case AdapterLoader.STATE_ERROR:
                contaier.setVisibility(View.VISIBLE);
                pb.setVisibility(View.GONE);
                content.setText("--- 加載更多失敗點擊重試 ---");
                contaier.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (mListener != null) {
                            mListener.onLoadMore();
                        }
                        content.setText("加載更多!!");
                        pb.setVisibility(View.VISIBLE);
                    }
                });
                break;
        }
    }

}

4.定義相關擴展方法便於用戶自己定義底部布局及相關狀態處理。

這裡就必須詳細講講Adapter裡面的相關方法了!

getItemCount(),在RecycleView知道它一共有多少數量的Item需要展示,返回0之後不會執行剩余的方法!

onCreateViewHolder(ViewGroup parent, int viewType),某種Type的Holder第一次創建的時候會調用該方法,當然沒有復用的時候也會去創建,一旦復用了,改方法不會再執行了!

onBindViewHolder(RecyclerView.ViewHolder holder, int position),每一次更新對應itemView的時候都會調用該方法,所以在該方法中要實時的刷新數據!(因為存在復用,所以刷新的時候一定要徹底!!!

以上三個方法是必須實現的,因為在父類adapter裡是抽象滴!

還有一個方法也比較重要:

getItemViewType(int position),這個方法是返回對應pos的類型的,如果你只有一個類型,不需要重寫該方法,默認返回的是0。

是不是這麼說起來比ListView還要爽一點兒?不用去判斷什麼convertView==null!

@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case TYPE_BOTTOM:
            if (loadMore != null) {
                RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);
                if (holder == null) {
                    throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");
                }
                return holder;
            } else {
                return new BottomViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_footer, parent, false));
            }
        default:
            return onViewHolderCreate(parent, viewType);
    }

}

@Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (getItemViewType(position) == TYPE_BOTTOM) {
        loadState = loadState == STATE_ERROR ? STATE_ERROR : isHasMore() ? STATE_LOADING : STATE_LASTED;
        if (loadMore != null) {
            try {
                onBottomViewHolderBind(holder, loadState);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try {
                ((BottomViewHolder) holder).bindDateView(loadState);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    } else {
        onViewHolderBind(holder, position);
    }
}

這裡在RefreshRecycleAdapter中實現了剛剛說的三個方法,並且相關的已經加final修飾了!所以之後你只需要實現如下方法來完成你自己的item填充就好了:

   RecyclerView.ViewHolder onViewHolderCreate(ViewGroup parent, int viewType);

void onViewHolderBind(RecyclerView.ViewHolder holder, int position);

對於加載更多的幾種狀態的更改,提供如下的相關方法!

boolean isHasMore();

void isLoadingMore();

void loadMoreError();

對於創建自己制定的加載更多的布局,提供如下方法擴展!

void setLoadMoreView(View view);

RecyclerView.ViewHolder onBottomViewHolderCreate(View loadMore);

void onBottomViewHolderBind(RecyclerView.ViewHolder holder, int loadState);

還沒有說的那就是數據源相關的方法。提供了set和append兩種方式!

void setList(List data);

void appendList(List data);

@Override
public final void appendList(List data) {
    int positionStart = list.size();
    list.addAll(data);
    int itemCount = list.size() - positionStart;

    if (positionStart == 0) {
        notifyDataSetChanged();
    } else {
        notifyItemRangeInserted(positionStart + 1, itemCount);
    }
}

還是那話,這些方法都是RefreshRecycleAdapter裡面寫好的,我們寫自己的adapter時更本不用去care!只需要去調用setList()或者appendList()就好了!!

說到這裡不得不提提RecycleView刷新數據的相關方法和坑!

在notifyDataSetChanged()的基礎上, RecycleView增加了一系列的方法用於增刪改。。

notifyItemInserted();
notifyItemRangeInserted();

notifyItemChanged();
notifyItemRangeChanged();

notifyItemRemoved();
notifyItemRangeRemoved();

在使用的過程中,發現調用notifyItemChanged()之後不會去執行onBindViewHolder(),(坑3 坑4)這個就導致刷新沒有觸發了!最後搜到的結果是因為mRecyclerView.setItemAnimator(new DefaultItemAnimator())引起的,解決方案是復寫相關方法

@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List

小結

首先通過addList()或者appendList()的方法設置相關數據源。

滑動過程中需要加載更多時回調相關方法,並在adapter中通知相關狀態刷新。

 adapter.isLoadingMore();
 if (null != listener) {
    listener.onLoadMore();
 }

加載錯誤的時候調用相關的方法通知狀態改變。

adapter.loadMoreError();

創建BottomHolder的時候判斷有沒有設置自定義的view,如果有,那麼就去走子類的onBottomViewHolderCreate()方法創建自定義的Bottomholder,然後實時更新相關數據!

    @Override
public final void setLoadMoreView(@NonNull View view) {
    loadMore = view;
}

if (loadMore != null) {
    RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);
    if (holder == null) {
        throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");
    }
    return holder;
        } 

最後在onBottomViewHolderBind(RecyclerView.ViewHolder holder, int state)的方法中執行bindDateView(state)實時刷新相關的狀態。

public void bindDateView(int state) {
    switch (state) {
        case AdapterLoader.STATE_LASTED:
            contaier.setVisibility(View.VISIBLE);
            contaier.setOnClickListener(null);
            pb.setVisibility(View.GONE);
            content.setText("---  沒有更多了  ---");
            break;
        case AdapterLoader.STATE_LOADING:
            contaier.setVisibility(View.VISIBLE);
            content.setText("加載更多!!");
            contaier.setOnClickListener(null);
            pb.setVisibility(View.VISIBLE);
            break;
        case AdapterLoader.STATE_ERROR:
            contaier.setVisibility(View.VISIBLE);
            pb.setVisibility(View.GONE);
            content.setText("--- 加載更多失敗點擊重試 ---");
            contaier.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mListener != null) {
                        mListener.onLoadMore();
                    }
                    content.setText("加載更多!!");
                    pb.setVisibility(View.VISIBLE);
                }
            });
            break;
    }
}

PS 最後還有默認是不顯示scrollBar的問題,這個問題,似乎必須在xml裡面配置,不能代碼直接new RecycleView。然後可以使用相關代碼控制ScrollBar是否顯示!

mRecyclerView.setVerticalScrollBarEnabled(true)


另外對於特性8、10這裡就不詳細介紹了,滑動刪除和拖拽排序在TouchHelperCallback中有相關支持!方法如下:

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    if (callBack != null) {
        callBack.onItemMove(viewHolder.getAdapterPosition(),
                target.getAdapterPosition());
    }
    return true;
}

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    if (callBack != null) {
        callBack.onItemDismiss(viewHolder.getAdapterPosition());
    }
}

詳細的可以參照相關Demo-FangShiActivity


gradle快速集成

  compile 'com.lovejjfg.powerrecycle:powerrecycle:1.0.0'

相關下載

演示Demo下載

項目中的使用

—- Edit By Joe —-

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved