Android RecyclerView 自動載入更多
老規矩,先上效果圖。
1 判斷到達底部
首先思考下自動載入更多這個需求,可以知道就是滑動到底部的時候同時載入更多的資料。那麼首先需要做的就是判斷是否滑動到了底部。
RecyclerView 有個方法onScrolled(int dx, int dy)會在整個滑動過程呼叫,所以我們可以在這個方法中去判斷是否到達底部。
至於要怎麼判斷呢?
到達底部即是說我們的列表的最底部已經展示了最後一條資料,這一條資料的position我們是知道的,即條目總數-1。
如果我們能獲取到滑動過程中,螢幕上顯示的最後一條的position,如果它的值等於條目總數-1的話,這個時候就說明已經滑動到最底部了。
看程式碼:
@Override
public void onScrolled(int dx, int dy) {
super.onScrolled(dx, dy);
//拿到最後一條的position
int endCompletelyPosition = getLayoutManager().
findLastCompletelyVisibleItemPosition();
if (endCompletelyPosition ==getAdapter().getItemCount()-1){
//執行載入更多的方法,無論是用介面還是別的方式都行
}
}
我這兒是重寫的RecyclerView,如果不想重寫,也可以用RecyclerView的addOnScrollListener方法,都差不多的。
2 新增底部的View
大多數情況下,我們載入更多的或者下拉重新整理的時候,都要有提示才合理。用ListView的話,可以直接用addFooterView方法,但是RecyclerView沒有這個方法,該怎麼辦呢。
這裡有一種方法是根據itemType來判斷,如果是最後一條,就新增的是底部View,其餘的是正常的View。
需要重寫Adapter 裡面的 getItemViewType(int position) 方法。
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1){
return ITEM_TYPE_FOOTER;
}else {
return 1;
}
}
然後在onCreateViewHolder方法裡面,新增底部View
@Override
public CommonRcViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE_FOOTER){
View view = 底部View;//可以通過layoutInflater獲取
return new CommonRcViewHolder(view);
}elese
return super.onCreateViewHolder(parent, viewType);
}
在onBindViewHolder方法中設定資料的時候也要判斷一下
@Override
public void onBindViewHolder(CommonRcViewHolder holder, int position) {
if (getItemViewType(position) != ITEM_TYPE_FOOTER){
//載入資料
}
}
大體上就是這樣了。
這裡我自己封裝了一個簡單的
LoadMoreRecyclerView.java
package cn.demo.videolist.recycler;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
public class LoadMoreRecyclerView extends RecyclerView {
private LinearLayoutManager mLinearLayoutManager;
private LoadMoreAdapter mAdapter;
public LoadMoreRecyclerView(Context context) {
this(context,null);
}
public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onScrolled(int dx, int dy) {
super.onScrolled(dx, dy);
int endCompletelyPosition = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
if (endCompletelyPosition == mAdapter.getItemCount()-1){
mAdapter.loadMore();
}
}
public void setManager(){
mLinearLayoutManager = new LinearLayoutManager(getContext());
setLayoutManager(mLinearLayoutManager);
}
public LinearLayoutManager getLayoutManager() {
return mLinearLayoutManager;
}
public void setLoadMoreAdapter(LoadMoreAdapter mAdapter) {
this.mAdapter = mAdapter;
setAdapter(mAdapter);
}
}
LoadMoreAdapter.java
package cn.demo.videolist.recycler;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import cn.demo.videolist.R;
public abstract class LoadMoreAdapter extends RecyclerView.Adapter<CommonRcViewHolder> {
public static final int ITEM_TYPE_FOOTER = 0;
protected String loadMoreText = "載入更多";
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1){
return ITEM_TYPE_FOOTER;
}else {
return 1;
}
}
@Override
public CommonRcViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE_FOOTER){
View view = LayoutInflater.from(parent.getContext()).inflate(getFooterViewResId(),parent,false);
return new CommonRcViewHolder(view);
}else {
return getViewHolder(parent,viewType);
}
}
@Override
public void onBindViewHolder(CommonRcViewHolder holder, int position) {
if (getItemViewType(position) != ITEM_TYPE_FOOTER){
loadData(holder, position);
}else {
TextView tv = holder.getView(getFooterTextViewResId());
tv.setText(loadMoreText);
}
}
@Override
public int getItemCount() {
return getCount()+1;
}
public void setLoadMoreText(String loadMoreText) {
this.loadMoreText = loadMoreText;
notifyItemChanged(getItemCount()-1);
}
public abstract int getFooterViewResId();
public abstract int getFooterTextViewResId();
public abstract int getCount();
public abstract CommonRcViewHolder getViewHolder(ViewGroup parent, int viewType);
public abstract void loadData(CommonRcViewHolder holder, int position);
public abstract void loadMore();
}
上面用到的一個通用的ViewHolder,當然也可以自己換別的ViewHolder,畢竟我這兒沒把點選做進去
CommonRcViewHolder.java
package cn.demo.videolist.recycler;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class CommonRcViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> views = new SparseArray<>();
private View view;
public CommonRcViewHolder(View itemView ) {
super(itemView);
view = itemView;
}
public <T extends View> T getView(int viewId){
View v = views.get(viewId);
if (v==null){
v = view.findViewById(viewId);
views.put(viewId, v);
}
return (T)v;
}
public <T extends View> T getViewWithLayoutParams(int viewId,ViewGroup.LayoutParams lp){
View v = views.get(viewId);
if (v==null){
v = view.findViewById(viewId);
v.setLayoutParams(lp);
views.put(viewId,v);
}
return (T)v;
}
public CommonRcViewHolder setText(int viewId,String text){
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
}
用法也很簡單,關鍵的幾步有
recyclerView = (LoadMoreRecyclerView) findViewById(R.id.recyclerView);
recyclerView.setManager();
final Adapter adapter = new Adapter();
recyclerView.setLoadMoreAdapter(adapter);
重寫的Adapter要繼承自上面的LoadMoreAdapter
Adapter
class Adapter extends LoadMoreAdapter{
private LinkedList<String> mData;
public Adapter( ) {
mData = new LinkedList<>();
for (int i = 0; i < 20; i++) {
mData.add("item "+i+"");
}
}
public void addDate(){
for (int i = 0; i < 3; i++) {
mData.addFirst("refresh "+i);
}
recyclerView.getLayoutManager().scrollToPosition(0);
notifyItemRangeInserted(0,3);
}
@Override
public CommonRcViewHolder getViewHolder(ViewGroup parent, int viewType) {
View view = getLayoutInflater().inflate(R.layout.item_recycler, parent,false);
return new CommonRcViewHolder(view);
}
@Override
public void loadData(CommonRcViewHolder holder, int position) {
TextView tv = holder.getView(R.id.tv);
tv.setText(mData.get(position));
}
@Override
public void loadMore() {
int startPosition = getCount();
if (mData.size() < 100) {
for (int i = 0; i < 20; i++) {
mData.add("more "+i + "");
}
int endPosition = getCount()-1;
notifyItemRangeInserted(startPosition,20);
}else {
setLoadMoreText("沒有更多了");
}
}
@Override
public int getFooterViewResId() {
return R.layout.item_footer;
}
@Override
public int getFooterTextViewResId() {
return R.id.tv;
}
@Override
public int getCount() {
return mData.size();
}
}
兩個item檔案
item_recycler.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:id="@+id/tv"
android:textColor="#ffffff"
android:layout_width="match_parent"
android:layout_height="50dp"
xmlns:android="http://schemas.android.com/apk/res/android" />
item_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView android:text="載入更多"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="50dp"
xmlns:android="http://schemas.android.com/apk/res/android" />