那如同這個題目,這里面涉及的東西其實還是比較多的缝驳,RecycleView SwipeRefreshLayout连锯,下拉刷新(這個就是SwipeRefreshLayout的),加載更多用狱。
SwipeRefreshLayout
這個是Google自己封裝的一個下拉刷新的控件运怖,里面使用了5.0開始的嵌套滑動機制,有興趣的朋友可以去看看源碼夏伊!使用起來其實就涉及到以下方法:
setOnRefreshListener()
下拉刷新的相關(guān)回調(diào)摇展。
setRefresh()
通知是否開始刷新或者刷新完成。(坑1)
setColorSchemeColors()
loading的時候progressbar
的顏色,支持多個溺忧。
SwipeRefreshLayout的坑
進入頁面調(diào)用setRefresh(true)
,根本不顯示刷新的小圓圈咏连?!
簡單的說鲁森,這個就是在onCreate()
方法執(zhí)行的時候祟滴,view還沒有繪制出來,這個時候你設(shè)置刷新不刷新其實都一樣的歌溉,解決方法垄懂,post一下!
mRecyclerView.post(new Runnable() {
@Override
public void run() {
mRefreshLayout.setRefreshing(refresh);
}
});
RecycleView
RecycleView
其實出現(xiàn)都有一定的年頭了痛垛,前幾天公司來面試的居然說他還沒有用過草慧。。這個也是醉醉的榜晦!
RecycleView
是ListView
的強力升級冠蒋!加入了holder便于管理和復(fù)用相同的類型。
就我目前掌握的情況乾胶,RecycleView對于ListView有了以下的不同:
1抖剿、加入了LayoutManager
用用管理各種類型的布局,而且通過不同的布局可以實現(xiàn)橫向识窿、豎向、瀑布式的等各種復(fù)雜的布局喻频。
2缩宜、加入Holder
來管理相關(guān)布局和復(fù)用,對于每一種Type的View你都要創(chuàng)建一個對應(yīng)的Holder
來管理它!
3锻煌、取消了header和bottom布局妓布。
4、沒有現(xiàn)成的itemClick
回調(diào)宋梧。
5匣沼、引入了豐富的動畫效果。(坑4)
6捂龄、添加了豐富的數(shù)據(jù)刷新的方法释涛,可以局部刷新了!(坑3)
7倦沧、可自定義相關(guān)分割線唇撬。
8、支持swipe刪除和drag排序展融。(ItemTouchHelper
幫助類)
9窖认、默認是不顯示scrollBar
的(坑2)
10、可以設(shè)置不同類型holder占據(jù)不同的空間(ItemColumnSpan GridLayoutManager)
上面這些不是所有的都講愈污,其實本文主要涉及的就是相關(guān)adapter
耀态,里面對應(yīng)不同的holder
轮傍,及相關(guān)的封裝暂雹。然后說說踩了哪些坑。
基本思路
- 1创夜、明確什么時候開始加載更多?
下拉刷新就調(diào)用SwipeRefreshLayout
相關(guān)就好了杭跪,那么加載更多呢?這個就要自己去寫相關(guān)的布局了驰吓。然后第一個問題涧尿,什么時候加載更多侮东?谍憔?因為RecycleView
有各種布局桑嘶,所以判斷最后一個也是要區(qū)分不同的adapter的曙砂!
- 2娩梨、加載更多有多少種情況打肝?
大致有三種铅辞,正在加載更多谋国;加載更多錯誤葵礼;沒有更多數(shù)據(jù)了;
具體實現(xiàn)
1号阿、監(jiān)聽滑動,滿足條件開始加載更多鸳粉。
@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.定義相關(guān)的方法實時更新ViewHolder的三種狀態(tài)。
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.定義相關(guān)擴展方法便于用戶自己定義底部布局及相關(guān)狀態(tài)處理蝙泼。
這里就必須詳細講講Adapter里面的相關(guān)方法了!
getItemCount()劝枣,在RecycleView知道它一共有多少數(shù)量的Item需要展示汤踏,返回0之后不會執(zhí)行剩余的方法!
onCreateViewHolder(ViewGroup parent, int viewType),某種Type的Holder第一次創(chuàng)建的時候會調(diào)用該方法舔腾,當然沒有復(fù)用的時候也會去創(chuàng)建溪胶,一旦復(fù)用了,改方法不會再執(zhí)行了稳诚!
onBindViewHolder(RecyclerView.ViewHolder holder, int position),每一次更新對應(yīng)itemView的時候都會調(diào)用該方法哗脖,所以在該方法中要實時的刷新數(shù)據(jù)!(因為存在復(fù)用扳还,所以刷新的時候一定要徹底2疟堋!氨距!)
以上三個方法是必須實現(xiàn)的桑逝,因為在父類adapter里是抽象滴!
還有一個方法也比較重要:
getItemViewType(int position),這個方法是返回對應(yīng)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<T>
中實現(xiàn)了剛剛說的三個方法,并且相關(guān)的已經(jīng)加final修飾了勒奇!所以之后你只需要實現(xiàn)如下方法來完成你自己的item填充就好了:
RecyclerView.ViewHolder onViewHolderCreate(ViewGroup parent, int viewType);
void onViewHolderBind(RecyclerView.ViewHolder holder, int position);
對于加載更多的幾種狀態(tài)的更改预鬓,提供如下的相關(guān)方法!
boolean isHasMore();
void isLoadingMore();
void loadMoreError();
對于創(chuàng)建自己制定的加載更多的布局赊颠,提供如下方法擴展格二!
void setLoadMoreView(View view);
RecyclerView.ViewHolder onBottomViewHolderCreate(View loadMore);
void onBottomViewHolderBind(RecyclerView.ViewHolder holder, int loadState);
還沒有說的那就是數(shù)據(jù)源相關(guān)的方法。提供了set和append兩種方式巨税!
void setList(List<T> data);
void appendList(List<T> data);
@Override
public final void appendList(List<T> data) {
int positionStart = list.size();
list.addAll(data);
int itemCount = list.size() - positionStart;
if (positionStart == 0) {
notifyDataSetChanged();
} else {
notifyItemRangeInserted(positionStart + 1, itemCount);
}
}
還是那話蟋定,這些方法都是RefreshRecycleAdapter<T>
里面寫好的,我們寫自己的adapter時更本不用去care!只需要去調(diào)用setList()
或者appendList()
就好了2萏怼驶兜!
說到這里不得不提提RecycleView
刷新數(shù)據(jù)的相關(guān)方法和坑!
在notifyDataSetChanged()
的基礎(chǔ)上, RecycleView
增加了一系列的方法用于增刪改抄淑。所以不要再任性的一味使用notifyDataSetChanged()屠凶,這樣也不專業(yè)了!
notifyItemInserted();
notifyItemRangeInserted();
notifyItemChanged();
notifyItemRangeChanged();
notifyItemRemoved();
notifyItemRangeRemoved();
在使用的過程中肆资,發(fā)現(xiàn)調(diào)用notifyItemChanged()
之后不會去執(zhí)行onBindViewHolder()
矗愧,(坑3 坑4)這個就導致刷新沒有觸發(fā)了!最后搜到的結(jié)果是因為mRecyclerView.setItemAnimator(new DefaultItemAnimator())引起的郑原,解決方案是復(fù)寫相關(guān)方法
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List<Object> payloads) {
return true;
}
小結(jié)
首先通過addList()
或者appendList()
的方法設(shè)置相關(guān)數(shù)據(jù)源唉韭。
滑動過程中需要加載更多時回調(diào)相關(guān)方法,并在adapter中通知相關(guān)狀態(tài)刷新犯犁。
adapter.isLoadingMore();
if (null != listener) {
listener.onLoadMore();
}
加載錯誤的時候調(diào)用相關(guān)的方法通知狀態(tài)改變属愤。
adapter.loadMoreError();
創(chuàng)建BottomHolder
的時候判斷有沒有設(shè)置自定義的view,如果有,那么就去走子類的onBottomViewHolderCreate()
方法創(chuàng)建自定義的Bottomholder
,然后實時更新相關(guān)數(shù)據(jù)酸役!
@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)
的方法中執(zhí)行bindDateView(state)
實時刷新相關(guān)的狀態(tài)住诸。
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
事格。然后可以使用相關(guān)代碼控制ScrollBar
是否顯示惕艳!
mRecyclerView.setVerticalScrollBarEnabled(true)
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
另外對于特性8、10這里就不詳細介紹了驹愚,滑動刪除和拖拽排序在TouchHelperCallback
中有相關(guān)支持!方法如下:
@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());
}
}
詳細的可以參照相關(guān)Demo-FangShiActivity
gradle快速集成
compile 'com.lovejjfg.powerrecycle:powerrecycle:1.0.0'
相關(guān)下載
---- Edit By Joe ----