这是第「26篇」原创分享
朝阳杨少爷(ID:CY_YANG_DA_YE)
专注于Android领域的开发者
GitHub传送门 欢迎Star 下载
今天给大家带来一个对于Recycleview的终极封装包含头、尾。
背景
Recycleview可以说是我们最常用的控件之一,只要有列表几乎就会用到。但是有没有因为每次要重写一大堆一样的相同代码而感到烦恼和厌倦,我们就想到能不能把相同内容进行封装,答案当然是可以,根据我们目前项目里的需求,又加上了头、尾布局。
效果
首先我们通过几个GIF来展示一下我们实现的效果
以上三种状态我们都会通过后面的代码来详细说明。
代码实现
头布局
头布局没什么可说的,在ListView当中可以直接通过addHeaderView()进行添加头布局,但是对于Recyclerview当中,并没有这种直接添加头布局的方法,那么我们就需要通过Adapter当中来实现。
尾布局
尾布局,我们设定有三种不同的模式,这个在我们下拉加当中可能遇到的情况
1.自动加载 AUTO_LOAD
2.加载到底 LOAD_END
3.加载失败 LOAD_ERROR
根据不同的布局我们设置了不同的提示语
(>﹏<) 卖力加载中~
(>﹏<) 暂时就这么多了~
(>﹏<) 点我加载更多~
点击事件
因为Recycleview本身并没有点击事件,自然我们需要添加上点击事件,同时我们对头布局,尾布局都增加点击事件
完整的BaseRecycleViewAdapter代码如下:
public abstract class BaseRecycleViewAdapter<T> extends RecyclerView.Adapter {
public static final int TYPE_NORMAL = 0;
public static final int TYPE_HEADER = 1;
public static final int TYPE_FOOTER = 2;
public static final int TYPE_AUTO_MORE = 3;
private AutoMoreListener mMoreListener;
protected List<T> mList;
protected Context mContext;
protected OnItemClickListener<T> mOnItemClickListener;
protected OnItemLongClickListener mOnItemLongClickListener;
protected OnHeaderItemClickListener mOnHeaderItemClickListener;
private MORE_TYPE mMoreType = MORE_TYPE.AUTO_LOAD;
public enum MORE_TYPE {
AUTO_LOAD, LOAD_END, LOAD_ERROR
}
protected BaseRecycleViewAdapter(Context context, List<T> list) {
mContext = context;
mList = list;
}
protected int getRealPosition(int position) {
if (getViewHeaderResource() > 0) {
return position - 1;
} else {
return position;
}
}
@Override
public int getItemViewType(int position) {
if (getMoreLoadLayoutResId() > 0 && position == getItemCount() - 1) {
return TYPE_AUTO_MORE;
}
if (position == 0) {
if (mList == null || mList.isEmpty()) {
if (getViewHeaderResource() > 0) {
return TYPE_HEADER;
} else if (getFooterViewResource() > 0) {
return TYPE_FOOTER;
} else {
return TYPE_NORMAL;
}
} else {
if (getViewHeaderResource() > 0) {
return TYPE_HEADER;
} else {
return TYPE_NORMAL;
}
}
} else if (position == getItemCount() - 1) {
if (getFooterViewResource() > 0) {
return TYPE_FOOTER;
} else {
return TYPE_NORMAL;
}
} else {
return TYPE_NORMAL;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
return getRecycleViewHeaderHolder(LayoutInflater.from(mContext).inflate(getViewHeaderResource(), parent, false));
} else if (viewType == TYPE_FOOTER) {
return getRecycleViewFooterHolder(LayoutInflater.from(mContext).inflate(getFooterViewResource(), parent, false));
} else if (viewType == TYPE_AUTO_MORE) {
return new MoreLoadViewHolder(LayoutInflater.from(mContext).inflate(getMoreLoadLayoutResId(), parent, false));
} else {
return getRecycleViewHolder(LayoutInflater.from(mContext).inflate(getViewResource(), parent, false));
}
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
final int viewType = getItemViewType(position);
if (viewType == TYPE_AUTO_MORE) {
bindMoreViewHolder((MoreLoadViewHolder) holder);
return;
}
if (viewType == TYPE_HEADER) {
dataReadByHeader(holder, position);
if (mOnHeaderItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnHeaderItemClickListener.onHeaderClick(holder.itemView, position, mList.get(position));
}
});
}
} else if (viewType == TYPE_FOOTER) {
dataReadByFooter(holder, position);
} else {
final int pos = getRealPosition(position);
dataRead(holder, pos);
if (mOnItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (viewType != TYPE_NORMAL) {
return;
}
mOnItemClickListener.onItemClick(holder.itemView, position, mList.get(pos));
}
});
}
if (mOnItemLongClickListener != null) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (viewType != TYPE_NORMAL) {
return false;
} else {
mOnItemLongClickListener.onItemLongClick(holder.itemView, position, mList.get(pos));
return true;
}
}
});
}
}
}
@Override
public int getItemCount() {
int count = mList == null ? 0 : mList.size();
if (count == 0) {
return count;
}
if (getViewHeaderResource() > 0) {
count += 1;
}
if (getFooterViewResource() > 0) {
count += 1;
}
if (getMoreLoadLayoutResId() > 0) {
count += 1;
}
return count;
}
protected static class MoreLoadViewHolder extends RecyclerView.ViewHolder {
private TextView mTvMsg;
public MoreLoadViewHolder(View itemView) {
super(itemView);
mTvMsg = (TextView) itemView.findViewById(R.id.tv_more_loading);
}
}
protected int getMoreLoadLayoutResId() {
return R.layout.item_new_more_footer_layout;
}
private void bindMoreViewHolder(MoreLoadViewHolder holder) {
if (mMoreType == MORE_TYPE.AUTO_LOAD) {
holder.mTvMsg.setText(mContext.getString(R.string.list_index_auto_loading));
} else if (mMoreType == MORE_TYPE.LOAD_END) {
holder.mTvMsg.setText(getFooterTip());
} else if (mMoreType == MORE_TYPE.LOAD_ERROR) {
holder.mTvMsg.setText(mContext.getString(R.string.list_index_loading_error));
holder.mTvMsg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mMoreListener != null) {
setMoreType(MORE_TYPE.AUTO_LOAD);
notifyDataSetChanged();
mMoreListener.load();
}
}
});
}
}
protected abstract int getViewResource();
protected int getViewHeaderResource() {
return 0;
}
protected int getFooterViewResource() {
return 0;
}
protected RecyclerView.ViewHolder getRecycleViewHeaderHolder(View view) {
throw new NullPointerException("getRecycleViewHeaderHolderv not init");
}
protected RecyclerView.ViewHolder getRecycleViewFooterHolder(View view) {
throw new NullPointerException("getRecycleViewFooterHolder not init");
}
protected abstract RecyclerView.ViewHolder getRecycleViewHolder(View view);
protected abstract void dataRead(RecyclerView.ViewHolder holder, int position);
protected void dataReadByHeader(RecyclerView.ViewHolder holder, int position) {
throw new NullPointerException("dataReadByHolder not init");
}
protected void dataReadByFooter(RecyclerView.ViewHolder holder, int position) {
throw new NullPointerException("dataReadByFooter not init");
}
/**
* 由于不同列表底部显示的东西可能不一样 所以由子类传入
*
* @return
*/
protected String getFooterTip() {
return mContext.getString(R.string.list_index_loading_end);
}
public interface OnItemClickListener<T> {
void onItemClick(View view, int position, T entity);
}
public interface OnItemLongClickListener<T> {
void onItemLongClick(View view, int position, T entity);
}
public interface OnHeaderItemClickListener<T> {
void onHeaderClick(View view, int position, T entity);
}
public void setOnItemClickListener(OnItemClickListener<T> listener) {
mOnItemClickListener = listener;
}
public void setOnHeaderItemClickListener(OnHeaderItemClickListener<T> listener) {
mOnHeaderItemClickListener = listener;
}
public void setOnItemLongClickListener(OnItemLongClickListener<T> itemLongClickListener) {
mOnItemLongClickListener = itemLongClickListener;
}
public void setMoreType(MORE_TYPE moreType) {
mMoreType = moreType;
}
public MORE_TYPE getMoreType() {
return mMoreType;
}
public void setMoreListener(AutoMoreListener listener) {
mMoreListener = listener;
}
public interface AutoMoreListener {
void load();
}
}
有了这个BaseRecycleViewAdapter那么我们再写Adapter的时候,直接继承这个BaseRecycleViewAdapter就可以了
我是用Kotlin写的,习惯用Java的小伙伴自行转成Java
package demo.rlv.cehome.com.alldemo.view
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.View
import android.widget.TextView
import demo.rlv.cehome.com.myapplication.R
import demo.rlv.view.BaseRecycleViewAdapter
/**
* @author yangzc
* @data 2019/3/14 10:56
* @desc
*/
open class RlvAdapter(context: Context, list: List<String>) : BaseRecycleViewAdapter<String>(context, list as MutableList<String>?) {
override fun getViewResource(): Int {
return R.layout.item_normal
}
override fun getViewHeaderResource(): Int {
return return R.layout.item_head
}
override fun getRecycleViewHolder(view: View): RecyclerView.ViewHolder {
return RlvViewHolder(view)
}
override fun dataRead(holder: RecyclerView.ViewHolder, position: Int) {
val h = holder as RlvViewHolder
h.tv.text = mList[position]
}
override fun getRecycleViewHeaderHolder(view: View): RecyclerView.ViewHolder {
return RlvHeadViewHolder(view)
}
override fun dataReadByHeader(holder: RecyclerView.ViewHolder?, position: Int) {
val h = holder as RlvHeadViewHolder
h.tv_head.text = "这个是头部"
}
private inner class RlvViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal val tv: TextView = itemView.findViewById(R.id.tv)
}
private inner class RlvHeadViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal val tv_head: TextView = itemView.findViewById(R.id.tv_head)
}
}
项目地址
应习惯写Java的同学要求,更新了Java使用版本。
https://github.com/yang0range/BaseRecycleView