转载请标明出处:
http://blog.csdn.net/lisdye2/article/details/52674879
本文出自:【Alex_MaHao的博客】
项目中的源码已经共享到github,有需要者请移步【Alex_MaHao的github】
在ListView
中已经自带了添加头布局和添加底部布局的方法,但是在RecyclerView
中,却没有默认实现,这导致在实现一些特殊布局中不是那么的方便,本篇博客将通过封装Adapter
方式添加头部和底部布局。
首先看一下实现效果
在阅读本篇博客之前,推荐阅读上一篇博客 RecyclerView 封装Adapter之BaseRecyclerAdapter
在实现的过程中,我们首先想到的便是复用的问题,如果对于ListView
和RecylerView
掌握的还可的话,知道在一个条目显示时候,会复用之前已经隐藏的条目。如果添加头部或者底部的视图,他的样式肯定适合我们普通显示的条目是不同的,这时候,复用势必会抛出错误,那么如何解决呢?
细心的同学会发现,在创建RecyclerView.Adapter
时,其中的一个方法有些特别public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
,第一个参数是父控件,第二个参数,从参数的命名山可以理解为视图的类型,关键便就在此,我们可以根据viewType
的值不同,构造不同的ViewHoder
。 既然能够获取,肯定有指定的地方,继续寻找,发现了下面的方法public int getItemViewType(int position)
,根据索引值返回对应条目的类型。那么,一切都捋顺了。思路如下:
public int getItemViewType(int position)
,定义头部,底部,正常条目的常量,根据索引值进行返回。public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
中,通过viewType
的不同值创建不同的ViewHolder
.定义变量,添加头部布局和底部布局
首先,从简单的开始实现,如下
public abstract class BaseRecycleAdapter<T> extends RecyclerView.Adapter<BaseRecycleAdapter.BaseViewHolder> {
// 头部控件
private View mHeaderView;
// 底部控件
private View mFooterView;
private boolean isHasHeader = false;
private boolean isHasFooter = false;
/**
* 添加头部视图
* @param header
*/
public void setHeaderView(View header){
this.mHeaderView = header;
isHasHeader = true;
notifyDataSetChanged();
}
/**
* 添加底部视图
* @param footer
*/
public void setFooterView(View footer){
this.mFooterView = footer;
isHasFooter = true;
notifyDataSetChanged();
}
}
上面的代码都很简单,设置头部和底部布局,修改对应的标志。
定义不同的视图类型,用以确定viewType的值
// item 的三种类型
public static final int ITEM_TYPE_NORMAL = 0X1111; // 正常的item类型
public static final int ITEM_TYPE_HEADER = 0X1112; // header
public static final int ITEM_TYPE_FOOTER = 0X1113; // footer
定义了三种类型,因为对于item
条目的类型,无非正常的,头部,底部三种,当然如果有特殊需求,自行添加。
在getItemViewType()中,根据索引值,返回不同的布局类型
@Override
public int getItemViewType(int position) {
// 根据索引获取当前View的类型,以达到复用的目的
// 根据位置的索引,获取当前position的类型
if(isHasHeader&&position==0){
return ITEM_TYPE_HEADER;
}
if(isHasHeader&&isHasFooter&&position==datas.size()+1){
// 有头部和底部时,最后底部的应该等于size+!
return ITEM_TYPE_FOOTER;
}else if(!isHasHeader&&isHasFooter&&position==datas.size()){
// 没有头部,有底部,底部索引为size
return ITEM_TYPE_FOOTER;
}
return ITEM_TYPE_NORMAL;
}
在onCreateViewHolder()中,根据viewType
类型构造不同的ViewHolder
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType==ITEM_TYPE_FOOTER){
// 如果是底部类型,返回底部视图
return new BaseViewHolder(mFooterView);
}
if(viewType==ITEM_TYPE_HEADER){
return new BaseViewHolder(mHeaderView);
}
View view = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(),parent,false);
return new BaseViewHolder(view);
}
因为设置了头部布局或者是底部布局,大小要发生不同的变化
@Override
public int getItemCount() {
int size = datas.size();
if(isHasFooter)
size ++;
if(isHasHeader)
size++;
return size;
}
最后,绑定数据方法的定义
如果看了上一篇博客,会明白通过bindData()
方法,将数据的绑定交给了子类,同样,此时也是如此,不过我们需要添加一些基本的判断。
@Override
public void onBindViewHolder(BaseRecycleAdapter.BaseViewHolder holder, final int position) {
if(isHasHeader&&isHasFooter){
// 有头布局和底部时,向前便宜一个,且最后一个不能绑定数据
if(position==0 ||position==datas.size()+1){
return;
}
bindData(holder,position-1);
}
if(position!=0&&isHasHeader&&!isHasFooter){
// 有顶部,但没有底部
bindData(holder,position-1);
}
if(!isHasHeader&&isHasFooter){
// 没有顶部,但有底部
if(position==datas.size()){
return;
}
bindData(holder,position);
}
if(!isHasHeader&&!isHasFooter){
// 没有顶部,没有底部
bindData(holder,position);
}
}
基本的逻辑,当有头部时,基本的向子类的偏移量要+1,有底部布局,则最后一个position
不会回调。
public class PersonActivity extends AppCompatActivity {
private RecyclerView mRecycler;
private ArrayList mPersons;
private PersonAdapter mAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_person);
mRecycler = ((RecyclerView) findViewById(R.id.reycler));
LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRecycler.setLayoutManager(manager);
mPersons = new ArrayList<>();
for(int i = 0 ;i <50;i++){
mPersons.add(new Person(i+""));
}
mAdapter = new PersonAdapter(mPersons);
mRecycler.setAdapter(mAdapter);
// 设置头部布局
mAdapter.setHeaderView(createView(Color.BLUE,"HEADER"));
// 设置底部布局
mAdapter.setFooterView(createView(Color.RED,"FOOTER"));
}
/**
* 创建一个简单的布局
* @param color
* @param str
* @return
*/
public View createView(int color,String str){
TextView text = new TextView(this);
RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,100);
text.setLayoutParams(params);
text.setBackgroundColor(color);
text.setText(str);
text.setGravity(Gravity.CENTER);
return text;
}
}
public abstract class BaseRecycleAdapter<T> extends RecyclerView.Adapter<BaseRecycleAdapter.BaseViewHolder> {
protected List datas;
public BaseRecycleAdapter(List datas) {
this.datas = datas;
}
// 头部控件
private View mHeaderView;
// 底部控件
private View mFooterView;
// item 的三种类型
public static final int ITEM_TYPE_NORMAL = 0X1111; // 正常的item类型
public static final int ITEM_TYPE_HEADER = 0X1112; // header
public static final int ITEM_TYPE_FOOTER = 0X1113; // footer
private boolean isHasHeader = false;
private boolean isHasFooter = false;
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType==ITEM_TYPE_FOOTER){
// 如果是底部类型,返回底部视图
return new BaseViewHolder(mFooterView);
}
if(viewType==ITEM_TYPE_HEADER){
return new BaseViewHolder(mHeaderView);
}
View view = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(),parent,false);
return new BaseViewHolder(view);
}
@Override
public void onBindViewHolder(BaseRecycleAdapter.BaseViewHolder holder, final int position) {
if(isHasHeader&&isHasFooter){
// 有头布局和底部时,向前便宜一个,且最后一个不能绑定数据
if(position==0 ||position==datas.size()+1){
return;
}
bindData(holder,position-1);
}
if(position!=0&&isHasHeader&&!isHasFooter){
// 有顶部,但没有底部
bindData(holder,position-1);
}
if(!isHasHeader&&isHasFooter){
// 没有顶部,但有底部
if(position==datas.size()){
return;
}
bindData(holder,position);
}
if(!isHasHeader&&!isHasFooter){
// 没有顶部,没有底部
bindData(holder,position);
}
}
/**
* 添加头部视图
* @param header
*/
public void setHeaderView(View header){
this.mHeaderView = header;
isHasHeader = true;
notifyDataSetChanged();
}
/**
* 添加底部视图
* @param footer
*/
public void setFooterView(View footer){
this.mFooterView = footer;
isHasFooter = true;
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
// 根据索引获取当前View的类型,以达到复用的目的
// 根据位置的索引,获取当前position的类型
if(isHasHeader&&position==0){
return ITEM_TYPE_HEADER;
}
if(isHasHeader&&isHasFooter&&position==datas.size()+1){
// 有头部和底部时,最后底部的应该等于size+!
return ITEM_TYPE_FOOTER;
}else if(!isHasHeader&&isHasFooter&&position==datas.size()){
// 没有头部,有底部,底部索引为size
return ITEM_TYPE_FOOTER;
}
return ITEM_TYPE_NORMAL;
}
/**
* 刷新数据
* @param datas
*/
public void refresh(List datas){
this.datas.clear();
this.datas.addAll(datas);
notifyDataSetChanged();
}
/**
* 添加数据
* @param datas
*/
public void addData(List datas){
this.datas.addAll(datas);
notifyDataSetChanged();
}
/**
* 绑定数据
* @param holder 具体的viewHolder
* @param position 对应的索引
*/
protected abstract void bindData(BaseViewHolder holder, int position);
@Override
public int getItemCount() {
int size = datas.size();
if(isHasFooter)
size ++;
if(isHasHeader)
size++;
return size;
}
/**
* 封装ViewHolder ,子类可以直接使用
*/
public class BaseViewHolder extends RecyclerView.ViewHolder{
private Map mViewMap;
public BaseViewHolder(View itemView) {
super(itemView);
mViewMap = new HashMap<>();
}
/**
* 获取设置的view
* @param id
* @return
*/
public View getView(int id) {
View view = mViewMap.get(id);
if (view == null) {
view = itemView.findViewById(id);
mViewMap.put(id, view);
}
return view;
}
}
/**
* 获取子item
* @return
*/
public abstract int getLayoutId();
/**
* 设置文本属性
* @param view
* @param text
*/
public void setItemText(View view,String text){
if(view instanceof TextView){
((TextView) view).setText(text);
}
}
}