RecyclerView上拉刷新,下拉加载更多
经常在写项目的时候都会碰到RecyclerView上拉刷新,下拉加载更多的功能,今天自己来捋一捋。
效果图
下拉刷新(SwipeRefreshLayout)
这是Google自己出的下拉刷新控件,其实还挺好看的,这里我们只要知道其中几个方法就可以了。
1. setColorSchemeResources
2. setOnRefreshListener
第一个是设置刷新时小圆圈的颜色,可以设置4中颜色切换。
第二个方法是设置刷新监听。这个接口里面有个onRefresh方法,刷新任务就在这里面执行。
所以总而言之下拉刷新才用官方的还是so easy.
在刷新任务完成后,你就可以把新数注入到adapter里面,刷新下就可以完成下拉刷新。
上拉加载更多
首先需要判断是否滑动到底部了,所以写个抽象类,继承RecyclerView的OnScrollListener
public abstract class RecyclerOnScrollListener extends RecyclerView.OnScrollListener {
private int mPreviousTotal = 0;
private boolean mLoading = true;
int mFirstVisibleItem, mVisibleItemCount, mTotalItemCount;//第一个item,显示的item总数,整个item总数
private int currentPage = 1;
private LinearLayoutManager mLinearLayoutManager;
public RecyclerOnScrollListener(
LinearLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mVisibleItemCount = recyclerView.getChildCount();
mTotalItemCount = mLinearLayoutManager.getItemCount();
mFirstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (mLoading) {
if (mTotalItemCount > mPreviousTotal) {
mLoading = false;
mPreviousTotal = mTotalItemCount;
}
}
if (!mLoading
&& (mTotalItemCount - mVisibleItemCount) <= mFirstVisibleItem) {
currentPage++;
onLoadMore(currentPage);
mLoading = true;
}
}
public abstract void onLoadMore(int currentPage);
}
这个类主要是判断是否滚动到了底部,滚动到了底部就去执行加载更多方法
添加底部加载更多布局
因为加载更多是属于耗时操作,为了提高用户体验,所以很有必要加个进度条提示。
进度条可以当做为一个item,在滑动到最后的时候就让他显示出来。
这里我采用了牛人的一个类,来辅助我们实现这个效果。站在巨人的肩膀上就可以看得更远。
到时直接把自己的适配器传递进去就行了。先贴上大神的代码
public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int HEADERS_START = Integer.MIN_VALUE;
private static final int FOOTERS_START = Integer.MIN_VALUE + 10;
private static final int ITEMS_START = Integer.MIN_VALUE + 20;
private static final int ADAPTER_MAX_TYPES = 100;
private RecyclerView.Adapter mWrappedAdapter;
private List<View> mHeaderViews, mFooterViews;
private Map<Class, Integer> mItemTypesOffset;
/**
* Construct a new header view recycler adapter
* @param adapter The underlying adapter to wrap
*/
public HeaderViewRecyclerAdapter(RecyclerView.Adapter adapter) {
mHeaderViews = new ArrayList<View>();
mFooterViews = new ArrayList<View>();
mItemTypesOffset = new HashMap<Class, Integer>();
setWrappedAdapter(adapter);
}
/**
* Replaces the underlying adapter, notifying RecyclerView of changes
* @param adapter The new adapter to wrap
*/
public void setAdapter(RecyclerView.Adapter adapter) {
if(mWrappedAdapter != null && mWrappedAdapter.getItemCount() > 0) {
notifyItemRangeRemoved(getHeaderCount(), mWrappedAdapter.getItemCount());
}
setWrappedAdapter(adapter);
notifyItemRangeInserted(getHeaderCount(), mWrappedAdapter.getItemCount());
}
@Override
public int getItemViewType(int position) {
int hCount = getHeaderCount();
if (position < hCount) return HEADERS_START + position;
else {
int itemCount = mWrappedAdapter.getItemCount();
if (position < hCount + itemCount) {
return getAdapterTypeOffset() + mWrappedAdapter.getItemViewType(position - hCount);
}
else return FOOTERS_START + position - hCount - itemCount;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if (viewType < HEADERS_START + getHeaderCount())
return new StaticViewHolder(mHeaderViews.get(viewType - HEADERS_START));
else if (viewType < FOOTERS_START + getFooterCount())
return new StaticViewHolder(mFooterViews.get(viewType - FOOTERS_START));
else {
return mWrappedAdapter.onCreateViewHolder(viewGroup, viewType - getAdapterTypeOffset());
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
int hCount = getHeaderCount();
if (position >= hCount && position < hCount + mWrappedAdapter.getItemCount())
mWrappedAdapter.onBindViewHolder(viewHolder, position - hCount);
}
/**
* Add a static view to appear at the start of the RecyclerView. Headers are displayed in the
* order they were added.
* @param view The header view to add
*/
public void addHeaderView(View view) {
mHeaderViews.add(view);
}
/**
* Add a static view to appear at the end of the RecyclerView. Footers are displayed in the
* order they were added.
* @param view The footer view to add
*/
public void addFooterView(View view) {
mFooterViews.add(view);
}
@Override
public int getItemCount() {
return getHeaderCount() + getFooterCount() + getWrappedItemCount();
}
/**
* @return The item count in the underlying adapter
*/
public int getWrappedItemCount() {
return mWrappedAdapter.getItemCount();
}
/**
* @return The number of header views added
*/
public int getHeaderCount() {
return mHeaderViews.size();
}
/**
* @return The number of footer views added
*/
public int getFooterCount() {
return mFooterViews.size();
}
private void setWrappedAdapter(RecyclerView.Adapter adapter) {
if (mWrappedAdapter != null) mWrappedAdapter.unregisterAdapterDataObserver(mDataObserver);
mWrappedAdapter = adapter;
Class adapterClass = mWrappedAdapter.getClass();
if(!mItemTypesOffset.containsKey(adapterClass)) putAdapterTypeOffset(adapterClass);
mWrappedAdapter.registerAdapterDataObserver(mDataObserver);
}
private void putAdapterTypeOffset(Class adapterClass) {
mItemTypesOffset.put(adapterClass, ITEMS_START + mItemTypesOffset.size() * ADAPTER_MAX_TYPES);
}
private int getAdapterTypeOffset() {
return mItemTypesOffset.get(mWrappedAdapter.getClass());
}
private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
super.onItemRangeChanged(positionStart, itemCount);
notifyItemRangeChanged(positionStart + getHeaderCount(), itemCount);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
notifyItemRangeInserted(positionStart + getHeaderCount(), itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
super.onItemRangeRemoved(positionStart, itemCount);
notifyItemRangeRemoved(positionStart + getHeaderCount(), itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
super.onItemRangeMoved(fromPosition, toPosition, itemCount);
int hCount = getHeaderCount();
// TODO: No notifyItemRangeMoved method?
notifyItemRangeChanged(fromPosition + hCount, toPosition + hCount + itemCount);
}
};
private static class StaticViewHolder extends RecyclerView.ViewHolder {
public StaticViewHolder(View itemView) {
super(itemView);
}
}
}
这个类很好用,可以随意添加头和尾。它的主要原理也就是先获取源数据的item数,然后根据你添加的布局类型(头或者尾)去动态的添加到recyclerview里面。
最后贴上activity源代码吧,看看如何使用
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private SwipeRefreshLayout mRefreshLayout;
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private List<String> mLists;
private HeaderViewRecyclerAdapter mHeaderAdapter;
private List<String> list=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.refreshlayout);
mRecyclerView= (RecyclerView) findViewById(R.id.recyclerview);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRefreshLayout.setColorSchemeResources(
R.color.colorAccent,
R.color.colorPrimary,
R.color.read,
R.color.blue
);
mRefreshLayout.setOnRefreshListener(this);
initdata();
mAdapter=new MyAdapter(MainActivity.this,mLists);
mHeaderAdapter=new HeaderViewRecyclerAdapter(mAdapter);
mRecyclerView.setAdapter(mHeaderAdapter);
initFootView();
mRecyclerView.addOnScrollListener(new RecyclerOnScrollListener(linearLayoutManager) {
@Override
public void onLoadMore(int currentPage) {
loadMoreData();
}
});
}
private void initFootView() {
View loadMoreView = LayoutInflater
.from(MainActivity.this)
.inflate(R.layout.item_foot_layout, mRecyclerView, false);
mHeaderAdapter.addFooterView(loadMoreView);
}
private void initdata() {
mLists=new ArrayList<>();
for (int i=0;i<15;i++){
mLists.add(""+i+" itme");
}
}
/**
* 测试数据,延迟两秒执行
*/
private void loadMoreData(){
Handler handler=new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
loadData();
}
},2000);
}
private void loadData(){
list.clear();
for (int i=0;i<5;i++){
list.add("上拉加载出来的数据");
}
mLists.addAll(list);
mHeaderAdapter.notifyDataSetChanged();
}
@Override
public void onRefresh() {
Handler handler=new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
mRefreshLayout.setRefreshing(false);
Toast.makeText(MainActivity.this,"数据刷新了",Toast.LENGTH_SHORT).show();
}
},2000);
}
}
这里数据都是测试数据,实际上可以根据自己需求去获取的,获取完后添加到原数据里面,然后刷新一下就OK了(这里先不用考虑局部刷新……)。