Recyclerview上拉加载下拉刷新
目录:
1.需求与概述
2.解决方案分析
3.案例实现
1.需求与概述
上拉加载下拉刷新可以说是APP最普遍的一个需求了,当应用需要在页面加载 庞大的数据但又不能 一次 全部加载完的时候,边看边加载就必不可少了,而实现上拉加载可以通过我们的ListView去实现或者是通过我们今天的猪脚,RecyclerView。RecyclerView啥都好唯一不好的一点就是需要我们自己去写数据集的头部和底部。接下来我们就来分析一下实现这个需求我们需要的主要控件和作用是啥。
2.解决方案分析
我们的解决方案是SwipeRefreshLayout+RecyclerView,而SwipeRefreshLayout实现的是下拉刷新功能,当下拉触发的时候通过开启线程去加载新的数据;而上拉加载需要我们自己去实现,说到这儿,那我们应该怎么去实现呢?我们都知道RecyclerView的adapter中有个叫viewType的东西,如果还不了解viewType是干嘛的,可以看看我前两篇文章( ReyclerView花样布局 )和( RecyclerView彩虹瀑布流实现 ) 根据不同的viewType可以对应去加载不同的布局。这里上拉加载就是在每次滑动到最后一个item的时候去加载一个footerview,当每一个footerview加载出来的时候就去启动一个线程加载更多的数据显示,以此循环,当加载完数据之后又根据不同的viewType加载一个无数据的提示布局,并且不做加载更多的动作,大概就是这样。
3.案例实现
说总是那么不明不白,我们应该动手写,接下来就是小案例的代码,不过得先来个效果图。
(1)效果截图(图片来自堆糖,就喜欢这种调调)

要实现分页加载当然得先来点数据,接下来是数据的创建于分页。
(2) 数据实体类RefreshObject.java
package com.example.recyclerviewdemo;
import java.io.Serializable;
/** * Created by elimy on 2017-01-20.
*/
public class RefreshObject implements Serializable {
private int Image;
private String desc;
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getImage() {
return Image;
}
public void setImage(int image) {
Image = image;
}
public RefreshObject(int image, String desc) {
this.Image = image;
this.desc = desc;
}
}
AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码
(3)数据集合处理类GetDataPage.java
package com.example.recyclerviewdemo;
import java.lang.reflect.Field;
import java.util.ArrayList;
/** * 该类实现通过传入指定的单页数据大小和页面编号返回指定的数据集
* 简单模拟了后端分页数据的加载
* Created by elimy on 2017-01-20.
*/
public class GetDataByPage {
final int DEFAULT_SIZE = 5;
/* * 根据传入的页数,以及默认的大小,获取返回对应的数据
* */
public ArrayList<RefreshObject> getPageData(int pageNum) {
int size = DEFAULT_SIZE;
ArrayList<RefreshObject> pageList = new ArrayList<>();
//第一条数据的位置
int start_item = (pageNum - 1) * 5;
//最后一条数据的位置
int end_item = start_item + size - 1;
//获取到全部数据的ArrayList
ArrayList allList = createAllData();
//这里要注意最后一页的数据是否小于了页面默认的大小
if (pageNum <= allList.size() / size) {
for (int i = start_item; i <= end_item; i++) {
pageList.add((RefreshObject) allList.get(i));
}
} else if ((allList.size() % size) != 0) {
for (int i = start_item; i < allList.size(); i++) {
pageList.add((RefreshObject) allList.get(i));
}
}
return pageList;
}
public int getMaxPage(int size) {
int maxPage;
ArrayList<RefreshObject> allList = createAllData();
maxPage = (int) Math.ceil((double) allList.size() / size);
return maxPage;
}
/* * 这里重载了上面一个方法,作用雷同,只是这个方法用户可以自定义页面的大小
* */
public ArrayList<RefreshObject> getPageData(int size, int pageNum) {
ArrayList<RefreshObject> pageList = new ArrayList<>();
int start_item = (pageNum - 1) * size;
int end_item = start_item + size - 1;
ArrayList<RefreshObject> allList = createAllData();
if (pageNum <= allList.size() / size) {
for (int i = start_item; i <= end_item; i++) {
pageList.add(allList.get(i));
}
} else if ((allList.size() % size) != 0) {
for (int i = start_item + 1; i < allList.size(); i++) {
pageList.add(allList.get(i));
}
}
return pageList;
}
/* * 构造数据集合,这里为了方便我把图片资源名规则化,通过for循环添加进去,实际开发数据是由后端
* 提供没必要这么复杂
* */
private ArrayList<RefreshObject> createAllData() {
ArrayList<RefreshObject> allList = new ArrayList<>();
for (int i = 1; i <= 21; i++) {
String image_id;
if (i >= 10) {
image_id = "duitang" + i;
} else {
image_id = "duitang0" + i;
}
allList.add(new RefreshObject(getResId(image_id), "说点什么"));
}
for (int i = 1; i <= 10; i++) {
String image_id;
if (i >= 10) {
image_id = "movie" + i;
} else {
image_id = "movie0" + i;
}
allList.add(new RefreshObject(getResId(image_id), "说点什么"));
}
for (int i = 1; i <= 10; i++) {
String image_id;
if (i >= 10) {
image_id = "news" + i;
} else {
image_id = "news0" + i;
}
allList.add(new RefreshObject(getResId(image_id), "说点什么"));
}
return allList;
}
/* * 通过反射的方法根据图片String类型的id查找返回int类型的id
* */
public int getResId(String imageName) {
int resId = -1;
//获取到drawable类的类
Class drawable = R.drawable.class;
try {
//得到drawable中对应的图片名字段
Field field = drawable.getField(imageName);
//获取对应字段的int值
resId = field.getInt(imageName);
} catch (Exception e) {
e.printStackTrace();
}
return resId;
}
}
AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码
数据准备好,接下来是布局,我们一起来看看布局
(4)主布局refresh_recyclerview_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/refresh_recycler_view"
android:layout_width="match_parent"
android:scrollbars="vertical"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码
(5)单个item布局refresh_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="480dp">
<android.support.v7.widget.CardView
android:id="@+id/refresh_card"
app:cardCornerRadius="15dp"
app:cardPreventCornerOverlap="true"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardElevation="5dp"
app:contentPadding="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/news_linear_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--设置图片-->
<ImageView
android:id="@+id/refresh_image"
android:src="@mipmap/ic_launcher"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_weight="8"
android:layout_height="0dp" />
<!--设置描述文本-->
<TextView
android:id="@+id/refresh_desc"
android:textSize="20sp"
android:textColor="#000"
android:padding="4dp"
android:maxLines="2"
android:ellipsize="end"
android:paddingLeft="10dp"
tools:text="新闻描述一大堆"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码
(6)底部加载更多布局footer_refresh_item.xml和没有数据提示布局footer_no_more_item.xml
太简单就不贴了~~~~~~
布局和数据都准备好了,就等他们的红线,红线一上场那是千里姻缘一线牵啊。
(7) RecyclerView适配器类RefreshRecyclerViewAdapter.java
package com.example.recyclerviewdemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
/** * Created by elimy on 2017-01-20.
*/
public class RefreshRecyclerViewAdapter extends RecyclerView.Adapter {
private static final int TYPE_ITEM = 1;
private static final int TYPE_FOOTER = 2;
private static final int TYPE_NO_MORE = 3;
Context context;
ArrayList<RefreshObject> list;
public boolean flag = false;
public RefreshRecyclerViewAdapter(Context context, ArrayList<RefreshObject> list) {
this.context = context;
this.list = list;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
if (viewType == TYPE_ITEM) {
view = LayoutInflater.from(context).inflate(R.layout.refresh_item_layout, parent, false);
return new RefreshItemViewHolder(view);
} else if (viewType == TYPE_NO_MORE) {
view = LayoutInflater.from(context).inflate(R.layout.footer_no_more_item, parent, false);
return new RefreshFooterViewHolder(view);
} else {
view = LayoutInflater.from(context).inflate(R.layout.footer_refresh_item, parent, false);
return new RefreshFooterViewHolder(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof RefreshItemViewHolder) {
((RefreshItemViewHolder) holder).image.setImageResource(list.get(position).getImage());
((RefreshItemViewHolder) holder).refresh_desc.setText(list.get(position).getDesc());
}
}
/* * 获取传入数据list的大小,这里因为最后要添加一个加载更多的布局所以加了一
* */
@Override
public int getItemCount() {
return list.size() + 1;
}
/* * 判断是否为最后一条,对应返回显示不同的布局类型
* */
@Override
public int getItemViewType(int position) {
//这里判断position是不是最后一个位置,由于下标从0开始,所以会是list数量减去1
if (position == getItemCount() - 1 && !flag) {
return TYPE_FOOTER;
} else if (position == getItemCount() - 1 && flag) {
return TYPE_NO_MORE;
} else {
return TYPE_ITEM;
}
}
/* * 下拉刷新添加数据
* */
public void addNewItem(ArrayList newData) {
list.addAll(newData);
notifyDataSetChanged();
}
/* * 上拉加载更多
* */
public void addMoreItem(ArrayList moreData) {
list.addAll(moreData);
notifyDataSetChanged();
}
/* * 上拉加载更多
* */
public void addMoreItem(ArrayList moreData, boolean flag) {
list.addAll(moreData);
this.flag = flag;
notifyDataSetChanged();
}
/* * 数据项ViewHolder自定义
* */
class RefreshItemViewHolder extends RecyclerView.ViewHolder {
ImageView image;
TextView refresh_desc;
public RefreshItemViewHolder(View itemView) {
super(itemView);
image = (ImageView) itemView.findViewById(R.id.refresh_image);
refresh_desc = (TextView) itemView.findViewById(R.id.refresh_desc);
}
}
/* * 底部刷新Item的自定ViewHolder
* 由于不需要从后端逻辑层传递绑定数据,所以这里无需建立UI布局
* 映射
* */
class RefreshFooterViewHolder extends RecyclerView.ViewHolder {
public RefreshFooterViewHolder(View itemView) {
super(itemView);
}
}
}
AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码
(8) 主布局java类RefreshRecyclerActivity.java
package com.example.recyclerviewdemo;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.TypedValue;
import android.widget.Toast;
import java.util.ArrayList;
/** * Created by elimy on 2017-01-20.
*/
public class RefreshRcyclerActivity extends AppCompatActivity {
RecyclerView refreshRecyclerView;
SwipeRefreshLayout swipeRefreshLayout;
RefreshRecyclerViewAdapter adapter;
GetDataByPage getDataByPage;
int lastVisibleItem;
int currentPage = 1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.refresh_recyclerview_main);
//绑定布局
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
refreshRecyclerView = (RecyclerView) findViewById(R.id.refresh_recycler_view);
//设置进度条背景色
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(android.R.color.black);
//设置SwipeRefreshLayout圆形进度条旋转时的颜色过渡
swipeRefreshLayout.setColorSchemeColors(R.color.bgColor3, R.color.bgColor7);
//设置初始时显示加载进度条
swipeRefreshLayout.setProgressViewOffset(false, 0, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
//设置布局
/* refreshRecyclerView.setLayoutManager(new LinearLayoutManager(
this,LinearLayoutManager.VERTICAL,false));*/
final LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
refreshRecyclerView.setLayoutManager(mLayoutManager);
//实例化获取数据类GetDataByPage
getDataByPage = new GetDataByPage();
//实例化适配器类
adapter = new RefreshRecyclerViewAdapter(this,
(ArrayList<RefreshObject>) getDataByPage.getPageData(5, currentPage));
Log.d(" refresh", currentPage + "");
currentPage = currentPage + 1;
//设置适配器
refreshRecyclerView.setAdapter(adapter);
//设置RecyclerView滑动监听,实现上拉加载
refreshRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//如果停止滑动且最后一个可见的item的位置是数据长度-1
if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == adapter.getItemCount()) {
if (currentPage <= getDataByPage.getMaxPage(5)) {
//通过handler开启一个线程加载数据模拟网络请求
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
ArrayList newData;
//判断是否还有数据可加载
if (currentPage < getDataByPage.getMaxPage(5)) {
newData = getDataByPage.getPageData(5, currentPage);
adapter.addMoreItem(newData);
} else {
newData = getDataByPage.getPageData(5, currentPage);
adapter.addMoreItem(newData, true);
}
Log.d("more refresh", currentPage + "");
currentPage = currentPage + 1;
}
}, 3000);
} else {
Log.d("LOG", "没有数据了");
}
}
;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
}
});
//设置swipeRefreshLayout的下拉刷新
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//判断是否还有数据可加载
if (currentPage <= getDataByPage.getMaxPage(5)) {
ArrayList newData = getDataByPage.getPageData(5, currentPage);
adapter.addNewItem(newData);
Log.d("pull refresh", currentPage + "");
currentPage = currentPage + 1;
swipeRefreshLayout.setRefreshing(false);
} else {
Toast.makeText(RefreshRcyclerActivity.this, "没有数据了", Toast.LENGTH_SHORT).show();
swipeRefreshLayout.setRefreshing(false);
}
}
}, 3000);
}
});
}
}
AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码AI写代码
