目標
RecyclerView漸漸的取代了ListView,但是有一點它沒有ListView方便,那就是不能添加header,所以我們只能自己實現,功能如下:
- 手動添加刪除Header和Footer
- 提供加載更多接口
思路
實現該功能的核心在Adapter類,我們聲明三種類型來區分header、footer、normal。
private static final int TYPE_NORMAL = 0;
private static final int TYPE_HEADER = 1;
private static final int TYPE_FOOTER = 2;
重寫getItemViewType方法,根據當前mHeader和mFooter是否存在和position來判斷當前的type
@Override
public int getItemViewType(int position) {
if (mHeader == null && mFooter == null) {
return TYPE_NORMAL;
} else if (mHeader == null) {
return position == getItemCount() - 1 ? TYPE_FOOTER : TYPE_NORMAL;
} else if (mFooter == null) {
return position == 0 ? TYPE_HEADER : TYPE_NORMAL;
} else {
if (position == 0) {
return TYPE_HEADER;
}
if (position == getItemCount() - 1) {
return TYPE_FOOTER;
}
return TYPE_NORMAL;
}
}
onCreateViewHolder()很簡單,如下
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
return new HeaderViewHodler(mHeader);
case TYPE_FOOTER:
return new FooterViewHodler(mFooter);
case TYPE_NORMAL:
return new ItemViewHodler(mInflater.inflate(R.layout.item_layout, parent, false));
default:
return null;
}
}
提供onLoadMoreListener接口
public interface onLoadMoreListener {
public void onLoadMore(View footView);
}
...
public void setLoadMoreListener(onLoadMoreListener mLoadMoreListener) {
this.mLoadMoreListener = mLoadMoreListener;
}
接下來就是判斷是否到達底部,如果到達了那就調用loadMore接口
private void setupScrollListener() {
mLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.d("Debug", "count" + getItemCount() + "\nlast" + mLayoutManager.findLastVisibleItemPosition());
if (!isLoading && mFooter != null && mLayoutManager.findLastVisibleItemPosition() == getItemCount() - 1 && mLoadMoreListener != null) {
isLoading = true;
mLoadMoreListener.onLoadMore(mFooter);
}
}
});
}
這裏我們好需要寫一個getRealPosition方法,因爲可能我們需要點擊一個item然後通過接口傳遞給外部當前的position,用戶需要得到的是真正的數據位置,是除去header的(和footer無關,因爲footer總是在最後一個位置,不會影響前面的位置)。
private int getRealPosition(int position) {
return mHeader == null ? position : position - 1;
}
完整代碼
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_NORMAL = 0;
private static final int TYPE_HEADER = 1;
private static final int TYPE_FOOTER = 2;
public interface onLoadMoreListener {
public void onLoadMore(View footView);
}
public interface onItemClickListener {
public void onItemClick(View view, int position);
}
private List<String> mData;
private Context mContext;
private LayoutInflater mInflater;
private View mHeader;
private View mFooter;
private onLoadMoreListener mLoadMoreListener;
private onItemClickListener mClickListener;
private RecyclerView mRecyclerView;
private boolean isLoading = false;
private LinearLayoutManager mLayoutManager;
public MyAdapter(Context context, List<String> data, RecyclerView recyclerView) {
mContext = context;
mData = data;
mRecyclerView = recyclerView;
mInflater = LayoutInflater.from(context);
setupScrollListener();
}
private void setupScrollListener() {
mLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.d("Debug", "count" + getItemCount() + "\nlast" + mLayoutManager.findLastVisibleItemPosition());
if (!isLoading && mFooter != null && mLayoutManager.findLastVisibleItemPosition() == getItemCount() - 1 && mLoadMoreListener != null) {
isLoading = true;
mLoadMoreListener.onLoadMore(mFooter);
}
}
});
}
public void setLoading(boolean loading) {
isLoading = loading;
}
public void setClickListener(onItemClickListener mClickListener) {
this.mClickListener = mClickListener;
}
public void setLoadMoreListener(onLoadMoreListener mLoadMoreListener) {
this.mLoadMoreListener = mLoadMoreListener;
}
public void setHeader(View header) {
this.mHeader = header;
notifyDataSetChanged();
}
public void setFooter(View footer) {
this.mFooter = footer;
notifyDataSetChanged();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
return new HeaderViewHodler(mHeader);
case TYPE_FOOTER:
return new FooterViewHodler(mFooter);
case TYPE_NORMAL:
return new ItemViewHodler(mInflater.inflate(R.layout.item_layout, parent, false));
default:
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int type = getItemViewType(position);
if (type == TYPE_NORMAL) {
ItemViewHodler viewHolder = (ItemViewHodler)holder;
viewHolder.text.setText(mData.get(getRealPosition(position)));
}
}
@Override
public int getItemViewType(int position) {
if (mHeader == null && mFooter == null) {
return TYPE_NORMAL;
} else if (mHeader == null) {
return position == getItemCount() - 1 ? TYPE_FOOTER : TYPE_NORMAL;
} else if (mFooter == null) {
return position == 0 ? TYPE_HEADER : TYPE_NORMAL;
} else {
if (position == 0) {
return TYPE_HEADER;
}
if (position == getItemCount() - 1) {
return TYPE_FOOTER;
}
return TYPE_NORMAL;
}
}
@Override
public int getItemCount() {
return (mHeader == null ? 0 : 1) + (mFooter == null ? 0 : 1) + mData.size();
}
class ItemViewHodler extends RecyclerView.ViewHolder {
public TextView text;
public ItemViewHodler(final View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.text);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mClickListener != null) {
mClickListener.onItemClick(itemView, getRealPosition(getLayoutPosition()));
}
}
});
}
}
class HeaderViewHodler extends RecyclerView.ViewHolder {
public HeaderViewHodler(View itemView) {
super(itemView);
}
}
class FooterViewHodler extends RecyclerView.ViewHolder {
public FooterViewHodler(final View itemView) {
super(itemView);
}
}
private int getRealPosition(int position) {
return mHeader == null ? position : position - 1;
}
}