返回 登录
0

仿照Listview 优雅实现 RecyclerView的 HeaderView 和 FootView

前言:

作为一个Listview的进阶产品 RecyclerView在很多时候使用起来并不是那么方便,而其中addFootViewaddHeaderView使用起来远没有 Listview的一句话来的简单,而目前大多数博文提供的思路都是在BaseAdapterViewType来做文章,这样做确实可以很好的实现我们想要的效果,而今天,我们就在这个基础上来进行一次封装,使得我们使用的方式变成下面这样

 recyclerView.addFootView(R.layout.foot_item);
 recyclerView.addHeaderView(R.layout.header_item);
 recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
 recyclerView.setAdapter(adapter)

通过上面,我们大概能知道,我们是自定义了一个RecyclerView,其余的用法和原生的 RecyclerView是没有什么区别的,如果你感兴趣,那么,就接着往下看吧。。。

分析

上面的效果到底是怎么实现的呢?恩?聪明的你有没有想到ListView呢?然后你去翻看ListView源码,我们可以发现,他的实现其实和我们想的应该是差不多的,通过一个HeaderViewListAdapter将我们setAdapter的适配器包裹起来,然后内部就可以通过我们的ViewType来加载headerViewFootView了,恩?这叫什么模式来着。。。应该是装饰者模式。。。哈哈,原来如此简单,只是我们不容易想到而已,那么接下来,我们就来撸一把。。。

撸码

其实想到了这个方法,那么接下来的撸码过程应该是很简单的,这里我只考虑 LinearLayoutManager这个布局管理器了,如果你要实现其他两种布局管理器的foot和header,可以去看看鸿洋大神的博客,Ok,多的不说了,直接贴代码。

import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class FootHeaderRecyclerView extends RecyclerView {
    //这里我们保存的是 layoutId 方便 createViewHolder
    List<integer> mHeaders = new ArrayList<>();
    List<integer> mFoots = new ArrayList<>();

    private Adapter mAdapter;

    public FootHeaderRecyclerView(Context context) {
        super(context);
    }

    public FootHeaderRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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

    public void addHeaderView(@LayoutRes int layoutId) {
        checkAdapter();
        mHeaders.add(layoutId);
    }

    private void checkAdapter() {
        if (!(mAdapter == null || mAdapter instanceof AdapterWrapper)) {
            throw new IllegalStateException("please call addHeaderView or addFootView before setAdapter");
        }
    }
    public void addFootView(@LayoutRes int layoutId) {
        checkAdapter();
        mFoots.add(layoutId);
    }
    //关键在这里我们对 adapter 进行了包装
    @Override
    public void setAdapter(Adapter adapter) {
        Adapter adapter1;
        if (mFoots.size() != 0 || mHeaders.size() != 0) {
             adapter1 = new AdapterWrapper(adapter);
             super.setAdapter(adapter1);
        } else {
            super.setAdapter(adapter);
            adapter1 = adapter;
        }
        mAdapter =adapter1;
    }

    private class AdapterWrapper extends Adapter {
        RecyclerView.Adapter adapter;

        public AdapterWrapper(Adapter adapter) {
            this.adapter = adapter;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            if (mHeaders.contains(viewType) || mFoots.contains(viewType)) {
                return new ViewHolder(inflater.inflate(viewType, parent, false)) {
                };
            } else {
                return   adapter.createViewHolder(parent, viewType);
            }
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            //此处应该注意!!!
            // 和 listview 一样这里的 position 是包含了 foot 和 header 的
            adapter.onBindViewHolder(holder, position);
        }

        @Override
        public int getItemCount() {
            return adapter.getItemCount() + mHeaders.size() + mFoots.size();
        }
        //稍微有点障碍的可能就是这个 position 的计算了,当然,其实也很简单,代两个数进去计算就好了
        @Override
        public int getItemViewType(int position) {
            if (position < mHeaders.size()) {
                //header size
                return mHeaders.get(position);
            }
            if (position >= mHeaders.size() && position < adapter.getItemCount() + mHeaders.size()) {
                return adapter.getItemViewType(position - mHeaders.size());
            }

            if (position >= getItemCount() - mFoots.size()) {
                return mFoots.get(position - mHeaders.size() - adapter.getItemCount());
            }
            throw new RuntimeException("unknown error position");
        }
    }
}

结束语

今天分享这个,应该算是最简单的,但也不能算是简单,如果不是带着问题去看了ListView的源码,我觉的我肯定是想不出这个方法来实现的,记得以前看源码,是为了看源码而看源码,往往看来看去,也不知所云,而带着某种目的或者疑问去看源码,就会发现,很多东西都变的清晰,变的有迹可循,最重要的你会发现,看源码再也不是枯燥无味的事情了,而是那么的吸引着你。。。好了,最后,还是希望本文对你有一点帮助

评论