特別聲明:
一瞳氓、前言
- 話說RecyclerView已經(jīng)面市很久,也在很多應(yīng)用中得到廣泛的使用君旦,在整個(gè)開發(fā)者圈子里面也擁有很不錯(cuò)的口碑桩撮,那說明RecyclerView擁有比ListView,GridView之類控件有很多的優(yōu)點(diǎn)犬第,例如:數(shù)據(jù)綁定厦画,Item View創(chuàng)建,View的回收以及重用等機(jī)制。前三三篇文章已經(jīng)貢呢更新了以下三個(gè)部分:
1吝梅、RecyclerView控件的基本使用,包括基礎(chǔ),進(jìn)階,高級(jí)部分,動(dòng)畫之類(點(diǎn)擊進(jìn)入)
2、RecyclerView控件的實(shí)戰(zhàn)實(shí)例(點(diǎn)擊進(jìn)入)
3惹骂、RecyclerView控件集合AA(Android Annotations)注入框架實(shí)例(點(diǎn)擊進(jìn)入)
- 本來這個(gè)專題不打算更新苏携,不過前兩天看到各位童鞋還是挺積極的評(píng)論到,希望可以更新RecyclerView加入下拉刷新和上拉加載更多的功能对粪。正好昨天周末右冻,所以我這邊也就實(shí)現(xiàn)了這樣的功能,今天更新一下著拭。具體代碼已經(jīng)上傳到下面的項(xiàng)目中纱扭,歡迎各位去star和fork一下:
FastDev4Android框架項(xiàng)目地址:https://github.com/jiangqqlmj/FastDev4Android
- RecyclerView實(shí)現(xiàn)的列表,默認(rèn)情況下面是不帶下拉刷新和上拉記載更多效果的茫死,但是我在我們的實(shí)際項(xiàng)目當(dāng)中跪但,為了提高用戶體驗(yàn)履羞,這種效果一般都需要實(shí)現(xiàn)峦萎,在我以前的博客中已經(jīng)重寫了ListView(Android 列表下拉刷新組件PullToRefreshListView使用)實(shí)現(xiàn)上拉刷新和上拉加載更多效果÷啪茫現(xiàn)在我們已經(jīng)學(xué)會(huì)了ListView,GridView的替代品RecyclerView的基本使用方法,那么必不可少的也需要實(shí)現(xiàn)上拉刷新和上拉加載更多的效果了爱榔。今天我們會(huì)通過兩種方式來實(shí)現(xiàn)被环,具體會(huì)采用Android的另一控件SwipeRefreshLayout。
二详幽、SwipeRefreshLayout介紹
- SwipeRefrshLayout是Google官方更新的一個(gè)Widget,可以實(shí)現(xiàn)下拉刷新的效果筛欢。該控件集成自ViewGroup在support-v4兼容包下,不過我們需要升級(jí)supportlibrary的版本到19.1以上唇聘“婀茫基本使用的方法如下:
- setOnRefreshListener(OnRefreshListener):添加下拉刷新監(jiān)聽器
setRefreshing(boolean):顯示或者隱藏刷新進(jìn)度條
isRefreshing():檢查是否處于刷新狀態(tài)
setColorSchemeResources():設(shè)置進(jìn)度條的顏色主題,最多設(shè)置四種迟郎,以前的setColorScheme()方法已經(jīng)棄用了剥险。
具體使用效果下面我們會(huì)看到。
三宪肖、RecyclerView+SwpieRefreshLayout實(shí)現(xiàn)下拉刷新效果
1表制、SwipeRefreshLayout本身自帶下拉刷新的效果,那么我們可以選擇在RecyclerView布局外部嵌套一層SwipeRefreshLayout布局即可
- 具體布局文件如下:
<?xmlversionxmlversion="1.0" encoding="utf-8"?>
<LinearLayout xmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/demo_swiperefreshlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="vertical"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/demo_recycler"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
></android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
2控乾、接著在Activity中獲取SwipeRefreshLayout控件并且設(shè)置OnRefreshListener監(jiān)聽器么介,同時(shí)實(shí)現(xiàn)里邊的onRefresh()方法,在該方法中進(jìn)行網(wǎng)絡(luò)請(qǐng)求最新數(shù)據(jù)蜕衡,然后刷新RecyclerView列表同時(shí)設(shè)置SwipeRefreshLayout的進(jìn)度Bar的隱藏或者顯示效果壤短。
- 具體代碼如下:
demo_swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Log.d("zttjiangqq","invoke onRefresh...");
new Handler().postDelayed(newRunnable() {
@Override
public void run() {
List<String> newDatas = new ArrayList<String>();
for (int i = 0; i <5; i++) {
int index = i + 1;
newDatas.add("new item" + index);
}
adapter.addItem(newDatas);
demo_swiperefreshlayout.setRefreshing(false);
Toast.makeText(RecyclerRefreshActivity.this, "更新了五條數(shù)據(jù)...", Toast.LENGTH_SHORT).show();
}
}, 5000);
}
});
3、除此之外我們也來看一下Adapter和Activity中的其他代碼衷咽,也方便各位童鞋查看鸽扁。
RecyclerRefreshActivity.java
public class RecyclerRefreshActivity extends BaseActivity {
private SwipeRefreshLayout demo_swiperefreshlayout;
private RecyclerView demo_recycler;
private RefreshRecyclerAdapter adapter;
private LinearLayoutManager linearLayoutManager;
private int lastVisibleItem;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_refresh_layout);
demo_swiperefreshlayout=(SwipeRefreshLayout)this.findViewById(R.id.demo_swiperefreshlayout);
demo_recycler=(RecyclerView)this.findViewById(R.id.demo_recycler);
//設(shè)置刷新時(shí)動(dòng)畫的顏色,可以設(shè)置4個(gè)
demo_swiperefreshlayout.setProgressBackgroundColorSchemeResource(android.R.color.white);
demo_swiperefreshlayout.setColorSchemeResources(android.R.color.holo_blue_light,
android.R.color.holo_red_light,android.R.color.holo_orange_light,
android.R.color.holo_green_light);
demo_swiperefreshlayout.setProgressViewOffset(false, 0, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources()
.getDisplayMetrics()));
linearLayoutManager=new LinearLayoutManager(this);
linearLayoutManager.setOrientation(OrientationHelper.VERTICAL);
demo_recycler.setLayoutManager(linearLayoutManager);
//添加分隔線
demo_recycler.addItemDecoration(new AdvanceDecoration(this, OrientationHelper.VERTICAL));
demo_recycler.setAdapter(adapter = new RefreshRecyclerAdapter(this));
//設(shè)置下拉刷新監(jiān)聽
demo_swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Log.d("zttjiangqq","invoke onRefresh...");
new Handler().postDelayed(newRunnable() {
@Override
public void run() {
List<String> newDatas = new ArrayList<String>();
for (int i = 0; i <5; i++) {
int index = i + 1;
newDatas.add("new item" + index);
}
adapter.addItem(newDatas);
demo_swiperefreshlayout.setRefreshing(false);
Toast.makeText(RecyclerRefreshActivity.this, "更新了五條數(shù)據(jù)...", Toast.LENGTH_SHORT).show();
}
}, 5000);
}
});
}
***RefreshRecyclerAdapter.java ***
public class RefreshRecyclerAdapter extends RecyclerView.Adapter<RefreshRecyclerAdapter.ViewHolder>{
private LayoutInflater mInflater;
private List<String> mTitles=null;
public RefreshRecyclerAdapter(Context context){
this.mInflater=LayoutInflater.from(context);
this.mTitles=new ArrayList<String>();
for (int i=0;i<20;i++){
int index=i+1;
mTitles.add("item"+index);
}
}
/**
* item顯示類型
* @param parent
* @param viewType
* @return
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
//這邊可以做一些屬性設(shè)置镶骗,甚至事件監(jiān)聽綁定
//view.setBackgroundColor(Color.RED);
ViewHolder viewHolder=new ViewHolder(view);
return viewHolder;
}
/**
* 數(shù)據(jù)的綁定顯示
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.item_tv.setText(mTitles.get(position));
holder.itemView.setTag(position);
}
@Override
public int getItemCount() {
return mTitles.size();
}
//自定義的ViewHolder桶现,持有每個(gè)Item的的所有界面元素
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView item_tv;
public ViewHolder(View view){
super(view);
item_tv = (TextView)view.findViewById(R.id.item_tv);
}
}
//添加數(shù)據(jù)
public void addItem(List<String> newDatas) {
//mTitles.add(position, data);
//notifyItemInserted(position);
newDatas.addAll(mTitles);
mTitles.removeAll(mTitles);
mTitles.addAll(newDatas);
notifyDataSetChanged();
}
public void addMoreItem(List<String> newDatas) {
mTitles.addAll(newDatas);
notifyDataSetChanged();
}
}
- 以上重要地方的注釋已經(jīng)加上去了。
4鼎姊、運(yùn)行效果大致如下:
如無法顯示圖片:點(diǎn)擊此鏈接查看
四骡和、RecyclerView設(shè)置滾動(dòng)事件加入上拉加載更多功能
- 下面我們?cè)賮砜碦ecyclerView和相關(guān)類的一些特性:
LayoutManger給我們提供了以下幾個(gè)方法來讓開發(fā)者方便的獲取到屏幕上面的頂部item和頂部item相關(guān)的信息:
- findFirstVisibleItemPosition()
- findFirstCompletlyVisibleItemPosition()
- findLastVisibleItemPosition()
- findLastCompletlyVisibleItemPosition()
同時(shí)通過Recycler.Adapter的getItemCount()方法可以輕松獲取到RecyclerView列表中Item View的個(gè)數(shù)。
- 那么下面我們通過監(jiān)聽滑動(dòng)(滾動(dòng))事件相寇,然后在里邊判斷是否已經(jīng)滑動(dòng)到最底部來加載更多的數(shù)據(jù)慰于,使用方法如下:
//RecyclerView滑動(dòng)監(jiān)聽
demo_recycler.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState ==RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 ==adapter.getItemCount()) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
List<String> newDatas = new ArrayList<String>();
for (int i = 0; i< 5; i++) {
int index = i +1;
newDatas.add("more item" + index);
}
adapter.addMoreItem(newDatas);
}
},1000);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView,dx, dy);
lastVisibleItem =linearLayoutManager.findLastVisibleItemPosition();
}
});
-
運(yùn)行效果如下:
如不能顯示效果圖:點(diǎn)擊此鏈接查看
五、升級(jí)RecyclerView加入FootView實(shí)現(xiàn)上拉加載
- 上面我們雖然已經(jīng)實(shí)現(xiàn)了上拉加載更多的效果唤衫,但是還比較丑陋婆赠,最起碼要讓用戶知道確實(shí)在上拉加載的過程吧,例如加載一個(gè)底部的進(jìn)度布局佳励。這樣一想休里,那么我們就按照ListView方式addFootView()唄蛆挫,不過很可惜的是RecyclerView沒有給我們提供addFootView()方法,那該怎么樣辦呢妙黍?
我們來看RecyclerView.Apapter類:
-
我們要實(shí)現(xiàn)一個(gè)自定義Adapter一定需要實(shí)現(xiàn)onCreateViewHolder(ViewGroup paren,int viewType)方法悴侵,注意看方法中的第二個(gè)參數(shù)viewType,是不是想到布局類型了拭嫁,也就是說該也支持多套布局顯示的可免,那么查看基類中的所有方法如下:
上面有一個(gè)方法getItemType(),這個(gè)就和ListView的Adapter的實(shí)現(xiàn)差不多了,那么我們這邊可以使用多套布局給RecyclerView加入一個(gè)FootView布局即可做粤。
RefreshFootAdapter.Java具體實(shí)現(xiàn)流程如下:
1浇借、加入布局狀態(tài)標(biāo)志-用來判斷此時(shí)加載是普通Item還是foot view:
private static final int TYPE_ITEM =0; //普通Item View
private static final intTYPE_FOOTER = 1; //頂部FootView
2、重寫getItemCount()方法,返回的Item數(shù)量在數(shù)據(jù)的基礎(chǔ)上面+1怕品,增加一項(xiàng)FootView布局項(xiàng):
//返回的總條目數(shù) +1
public intgetItemCount() {
return mTitles.size()+1;
}
3逮刨、重寫getItemViewType方法來判斷返回加載的布局的類型:
public int getItemViewType(int position) {
// 最后一個(gè)item設(shè)置為footerView
if (position + 1 == getItemCount()) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
4、接著onCreateViewHolder(ViewGroup parent,int viewType)加載布局的時(shí)候根據(jù)viewType的類型來選擇指定的布局創(chuàng)建堵泽,返回即可:
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//進(jìn)行判斷顯示類型修己,來創(chuàng)建返回不同的View
if(viewType==TYPE_ITEM){
Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
//這邊可以做一些屬性設(shè)置,甚至事件監(jiān)聽綁定
//view.setBackgroundColor(Color.RED);
ItemViewHolder itemViewHolder=new ItemViewHolder(view);
return itemViewHolder;
}else if(viewType==TYPE_FOOTER){
Viewfoot_view=mInflater.inflate(R.layout.recycler_load_more_layout,parent,false);
//這邊可以做一些屬性設(shè)置迎罗,甚至事件監(jiān)聽綁定
//view.setBackgroundColor(Color.RED);
FootViewHolder footViewHolder=new FootViewHolder(foot_view);
return footViewHolder;
}
return null;
}
5睬愤、最后進(jìn)行判斷數(shù)據(jù)的時(shí)候(onBindViewHolder),判斷holder的類型來進(jìn)行判定數(shù)據(jù)即可:
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof ItemViewHolder) {
((ItemViewHolder)holder).item_tv.setText(mTitles.get(position));
holder.itemView.setTag(position);
}else if(holder instanceof FootViewHolder){
FootViewHolderfootViewHolder=(FootViewHolder)holder;
switch (load_more_status){
case PULLUP_LOAD_MORE:
footViewHolder.foot_view_item_tv.setText("上拉加載更多...");
break;
case LOADING_MORE:
footViewHolder.foot_view_item_tv.setText("正在加載更多數(shù)據(jù)...");
break;
}
}
}
6纹安、整個(gè)RefreshFootAdapter完整代碼如下:
- 同時(shí)該Adaper中我們還可以定義一個(gè)changeMoreStatus()方法和兩個(gè)字符串常量可以來進(jìn)行修改FootView中字符串提醒文本的尤辱。
public class RefreshFootAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
//上拉加載更多
public static final int PULLUP_LOAD_MORE=0;
//正在加載中
public static final int LOADING_MORE=1;
//上拉加載更多狀態(tài)-默認(rèn)為0
private int load_more_status=0;
private LayoutInflater mInflater;
private List<String> mTitles=null;
private static final intTYPE_ITEM = 0; //普通Item View
private static final intTYPE_FOOTER = 1; //頂部FootView
public RefreshFootAdapter(Context context){
this.mInflater=LayoutInflater.from(context);
this.mTitles=new ArrayList<String>();
for (int i=0;i<20;i++){
int index=i+1;
mTitles.add("item"+index);
}
}
/**
* item顯示類型
* @param parent
* @param viewType
* @return
*/
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//進(jìn)行判斷顯示類型,來創(chuàng)建返回不同的View
if(viewType==TYPE_ITEM){
Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
//這邊可以做一些屬性設(shè)置厢岂,甚至事件監(jiān)聽綁定
//view.setBackgroundColor(Color.RED);
ItemViewHolder itemViewHolder=new ItemViewHolder(view);
return itemViewHolder;
}else if(viewType==TYPE_FOOTER){
Viewfoot_view=mInflater.inflate(R.layout.recycler_load_more_layout,parent,false);
//這邊可以做一些屬性設(shè)置光督,甚至事件監(jiān)聽綁定
//view.setBackgroundColor(Color.RED);
FootViewHolder footViewHolder=new FootViewHolder(foot_view);
return footViewHolder;
}
return null;
}
/**
* 數(shù)據(jù)的綁定顯示
* @param holder
* @param position
*/
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof ItemViewHolder) {
((ItemViewHolder)holder).item_tv.setText(mTitles.get(position));
holder.itemView.setTag(position);
}else if(holder instanceof FootViewHolder){
FootViewHolder footViewHolder=(FootViewHolder)holder;
switch (load_more_status){
case PULLUP_LOAD_MORE:
footViewHolder.foot_view_item_tv.setText("上拉加載更多...");
break;
case LOADING_MORE:
footViewHolder.foot_view_item_tv.setText("正在加載更多數(shù)據(jù)...");
break;
}
}
}
/**
* 進(jìn)行判斷是普通Item視圖還是FootView視圖
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
// 最后一個(gè)item設(shè)置為footerView
if (position + 1 == getItemCount()) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
@Override
public int getItemCount() {
return mTitles.size()+1;
}
//自定義的ViewHolder,持有每個(gè)Item的的所有界面元素
public static class ItemViewHolder extends RecyclerView.ViewHolder {
public TextView item_tv;
public ItemViewHolder(View view){
super(view);
item_tv = (TextView)view.findViewById(R.id.item_tv);
}
}
/**
* 底部FootView布局
*/
public static class FootViewHolder extends RecyclerView.ViewHolder{
private TextView foot_view_item_tv;
public FootViewHolder(View view) {
super(view);
foot_view_item_tv=(TextView)view.findViewById(R.id.foot_view_item_tv);
}
}
//添加數(shù)據(jù)
public void addItem(List<String> newDatas) {
//mTitles.add(position, data);
//notifyItemInserted(position);
newDatas.addAll(mTitles);
mTitles.removeAll(mTitles);
mTitles.addAll(newDatas);
notifyDataSetChanged();
}
public void addMoreItem(List<String> newDatas) {
mTitles.addAll(newDatas);
notifyDataSetChanged();
}
/**
* //上拉加載更多
* PULLUP_LOAD_MORE=0;
* //正在加載中
* LOADING_MORE=1;
* //加載完成已經(jīng)沒有更多數(shù)據(jù)了
* NO_MORE_DATA=2;
* @param status
*/
public void changeMoreStatus(int status){
load_more_status=status;
notifyDataSetChanged();
}
}
7塔粒、整體Activity中還是設(shè)置監(jiān)聽RecyclerView的滾動(dòng)事件.代碼和第一個(gè)例子差不多,不過RecyclerView需要設(shè)置這邊的Adapter了,demo_recycler.setAdapter(adapter= new RefreshFootAdapter(this)):
//設(shè)置滑動(dòng)監(jiān)聽
demo_recycler.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState ==RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 ==adapter.getItemCount()) {
adapter.changeMoreStatus(RefreshFootAdapter.LOADING_MORE);
newHandler().postDelayed(new Runnable() {
@Override
public void run() {
List<String> newDatas = new ArrayList<String>();
for (int i = 0; i< 5; i++) {
int index = i +1;
newDatas.add("more item" + index);
}
adapter.addMoreItem(newDatas);
adapter.changeMoreStatus(RefreshFootAdapter.PULLUP_LOAD_MORE);
}
}, 2500);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView,dx, dy);
lastVisibleItem =linearLayoutManager.findLastVisibleItemPosition();
}
});
8结借、運(yùn)行效果大致如下:
六、最后總結(jié)
今天我們通過SwipeRefreshLayout和RecyclerView結(jié)合以及改造Adapter方式實(shí)現(xiàn)了RecyclerView的下拉刷新和上拉加載更多的效果卒茬。
本次具體實(shí)例注釋過的全部代碼已經(jīng)上傳到FastDev4Android項(xiàng)目中了船老。同時(shí)歡迎大家去Github站點(diǎn)進(jìn)行clone或者下載瀏覽:https://github.com/jiangqqlmj/FastDev4Android 同時(shí)歡迎大家star和fork整個(gè)開源快速開發(fā)框架項(xiàng)目~
本人錄制AA(android Annotations)注入框架的視頻教程已經(jīng)上線了,歡迎大家前往觀看。http://www.cniao5.com/course/10074
再次聲明:本文轉(zhuǎn)載自:【江清清的博客】http://blog.csdn.net/developer_jiangqq/article/details/49992269