RecyclerView学习(1) 添加头部和尾部

转载请注明出处 http://blog.csdn.net/u011453163/article/details/58586687

RecyclerView 已经不是一个陌生的组件了,但是相对于现在的项目在还是用比较老旧的ListView,虽然功能都能实现,但是毕竟得跟上时代变化嘛。

关于RecyclerView添加 头部和尾部,网上已经有各种大牛的版本,实现起来也不是很难。但是看一遍不如撸一遍。这里对学习RecyclerView过程中做一些记录,部分实现也有借鉴大牛们的思路,见笑。

先上个简单的效果图。

这里写图片描述

RecyclerView不像ListView 一样 提供了添加头部和尾部的方法,但是RecyclerView和ListView一样是支持多类型Item布局的,那么反过来 头部和尾部 也就只是一些特定类型的布局而已。所以我们可以通过
public int getItemViewType(int position) 算出头部和尾部的Type 来加载头部和尾部。

这里通过两种方式来实现这个效果,思路是一样的。

自定义一个 ExpandAdapter 继承 RecyclerView.Adapter

public class ExpandAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private RecyclerView.Adapter<RecyclerView.ViewHolder> adapter;

    public ExpandAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
        this.adapter = adapter;
    }

    @Override
    public int getItemViewType(int position) {

        return adapter.getItemViewType(position);
    }


    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        return adapter.onCreateViewHolder(parent, viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        adapter.onBindViewHolder(holder, position);
    }


    @Override
    public int getItemCount() {

        return adapter.getItemCount();
    } 
}

这里只是简单的做一个转换工作,以方便后期使用不需要去改动太多的东西。
1 通过 List 来实现。
接下来开始分析实现思路,首先要注意的是 public int getItemViewType(int position)默认是返回 0 的,
所以在定义头部和尾部的type是要错开 0 ,网上大部分大牛的思路都是定义一个比较大的起始值。

1 一个较大的起始值

private static final int TPEY_COUNT_START_TAG =100000;

2 两个存储头部和尾部 view的集合,和添加头部和尾部对应的方法

 List<View> headViews = new ArrayList<>();
 List<View> footViews = new ArrayList<>();

 public void addHeadView(View v) {
        if (headViews != null) {
            v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            headViews.add(v);
        }     
    }
    public void addFootView(View v) {
        if (footViews != null) {
            v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            footViews.add(v);
        }
    }

3 在 public int getItemViewType(int position) 计算返回的type

  public int getItemViewType(int position) {
        if (headViews.size() > 0) {
            if (position < headViews.size()) {
                return position+ TPEY_COUNT_START_TAG;
            }
        }
        if (footViews.size() > 0) {
            if (position >= getItemCount() - footViews.size()) {
                return position+ TPEY_COUNT_START_TAG;
            }
        }
        return adapter.getItemViewType(position);
    }

因为这里只有一个position参数可以使用,所以在position的基础上加个一个较大的值,这里是加法之后我们要做减法来算出下标 取出对应的 头部view和尾部view。

4 从List中取出渲染对应的头部和尾部。

定义头部和尾部对应的ViewHolder 便于理解

class HeaderHolder extends RecyclerView.ViewHolder {
        public HeaderHolder(View itemView) {
            super(itemView);
        }
    }

    class FootHolder extends RecyclerView.ViewHolder {
        public FootHolder(View itemView) {
            super(itemView);
        }
    }
@Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (headViews.size() > 0) {
            if (viewType- TPEY_COUNT_START_TAG >= 0 && viewType- TPEY_COUNT_START_TAG < headViews.size()) {
                return new HeaderHolder(headViews.get(viewType- TPEY_COUNT_START_TAG));
            }
        }
        if (footViews.size() > 0) {
            Log.d("ExpandAdapter", "viewType:" + viewType);
            if (viewType- TPEY_COUNT_START_TAG >= getItemCount() - footViews.size() && viewType- TPEY_COUNT_START_TAG < getItemCount()) {
                return new FootHolder(footViews.get(viewType- TPEY_COUNT_START_TAG -adapter.getItemCount()-headViews.size()));
            }
        }

        return adapter.onCreateViewHolder(parent, viewType);
    }

这里主要就是做一些判断 算出下标 来取出 headViews, footViews中对应的view。

5 绑定数据

  @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
        if(holder instanceof FootHolder ||holder instanceof HeaderHolder){
            return;
        }
        adapter.onBindViewHolder(holder, position);
    }

 @Override
    public int getItemCount() {
        return adapter.getItemCount() + headViews.size() + footViews.size();    
    }

动态添加的头部和尾部不需要在适配器里渲染数据,所以直接返回不管。Item总数就是头部和尾部和普通itme的总和,这个没必要说的。

6.由于RecyclerView 的布局形式不同。GridLayoutManager,StaggeredGridLayoutManager,LinearLayoutManager。为了适配这三者,还需要重写两个方法,这个是借鉴网上大牛的。

 @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder)
    {
        adapter.onViewAttachedToWindow(holder);
        int position = holder.getLayoutPosition();
        if ((position >= 0 && position < headViews.size())||(position>=getItemCount()-footViews.size()&&position<getItemCount()))
        {
            ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();

            if (lp != null
                    && lp instanceof StaggeredGridLayoutManager.LayoutParams)
            {

                StaggeredGridLayoutManager.LayoutParams p =
                        (StaggeredGridLayoutManager.LayoutParams) lp;

                p.setFullSpan(true);
            }
        }
    }
    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
            ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {

                @Override
                public int getSpanSize(int position) {
                    if (position >= 0 && position < headViews.size()) {
                        return ((GridLayoutManager) recyclerView.getLayoutManager()).getSpanCount();
                    }else if(position>=getItemCount()-footViews.size()&&position<getItemCount()){
                        return ((GridLayoutManager) recyclerView.getLayoutManager()).getSpanCount();
                    } else {
                        return 1;
                    }
                }
            });
        }
    }

StaggeredGridLayoutManager

这里写图片描述

GridLayoutManager

这里写图片描述

以上是使用List实现的方式,第二种的思路也是一样的,通过两个HashMap来存储头部和尾部 ,就不详细介绍了,主要列出一些区别。

 private static final int TPEY_HEADER_STATRT_INDEX =-100000;
 private static final int TPEY_FOOT_START_INDEX =100000;

 HashMap<Integer,View> headViewss=new HashMap<>();
 HashMap<Integer,View> footViewss=new HashMap<>();


  /**添加头部*/
  public void addHeadView(View v) {
        if(headViewss!=null){
            v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            headViewss.put(TPEY_HEADER_STATRT_INDEX+headViewss.size(),v);
        }
    }
    /**添加头部*/
    public void addFootView(View v) {

        if(footViewss!=null){
            v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            footViewss.put(TPEY_FOOT_START_INDEX+footViewss.size(),v);
        }
    }

/**重写适配器的两个方法也有区别 这里也可以同set 返回key值*/
   @Override
    public int getItemViewType(int position) {
        if(headViewss!=null&&position<headViewss.size()){
            return position+TPEY_HEADER_STATRT_INDEX;
        }
        if(footViewss!=null&&position>=adapter.getItemCount()+headViewss.size()){
            return TPEY_FOOT_START_INDEX+(position-adapter.getItemCount()-headViewss.size());
        }
        return adapter.getItemViewType(position);
    }

    /**通过Key直接获取*/
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
        if(headViewss.containsKey(viewType)){
            return new HeaderHolder(headViewss.get(viewType));
        }
        if(footViewss.containsKey(viewType)){
            return new FootHolder(footViewss.get(viewType));
        }
        return adapter.onCreateViewHolder(parent, viewType);
    }

/**此方法基本一样*/
 @Override
    public int getItemCount() {
       return adapter.getItemCount() + headViewss.size() + footViewss.size();
    }

这里因为是使用了ExpandAdapter 来做一个包装,所以相应的使用一个自定义RecyclerView来实现。以便我们不用做太多的改动。

public class ExpandRecyclerView extends RecyclerView{
    ExpandAdapter expandAdapter;
    public ExpandRecyclerView(Context context) {
        super(context);
    }
    public ExpandRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ExpandRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        expandAdapter =new ExpandAdapter(adapter);
        super.setAdapter(expandAdapter);
    }

    public void addHeadView(View v){
        if(expandAdapter !=null){
           expandAdapter.addHeadView(v);
           expandAdapter.notifyDataSetChanged();
        }
    }
    public void addFootView(View v){
        if(expandAdapter !=null){
            expandAdapter.addFootView(v);
            expandAdapter.notifyDataSetChanged();
        }
    }
}

使用自定义RecyclerView之后 我们不需要去改动适配器 还是该怎么使用就怎么使用。

到此,RecyclerView的添加头部和尾部就结束了。有什么不对的地方望指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值