(周期计划-2)RecyclerView封装系列(1):Header、Footer

2018年技术周期计划:周期计划-2(2018/1/8-2018/1/14)

写在前面

写这边文章的时候是2018年1月12日,也就是18年的第二周,按照原计划应该是关于分割线的封装,不过后来想一想既然提到了封装,那不如多写一写,因此有了这个封装系列。
这个系列的开篇是关于Header和Footer的封装思路。

感兴趣的看官,可以看看我的其他文章:
1、常用集合的源码分析:HashMap
2、Java反射实践:从反射中理解class
3、从公司项目配置看Gradle

开始

效果在这里就不浪费篇幅去展示了,估计大家也都知道。

正常思路

给RecyclerView增加头部布局本身不存在什么难度,按照我们正常的流程,写一个符合我们业务逻辑的头部布局的ViewHolder,然后在getItemViewType()里通过position返回一个表示头部布局ViewHolder的Type类型,然后在onCreateItemViewHolder(ViewGroup parent, int viewType)里通过viewType来初始化return我们不同的ViewHolder。

这用做法,肯定是没有什么毛病。但是如果我们需要频繁的使用不同的头部布局,那么我们就要考虑去封装这个问题了。这里简单的提供一种封装的思路,(当然这种思路是存在于弊端的,那就是这种封装是基于业务的封装,系列计划中会有关于抽取的封装思路)如果有更好的思路,欢迎讨论。


封装思路(有点水)

这里提供的思路并不具备可移植性,因为是基于业务本身的封装,所以核心是继承RecyclerView.Adapter并拓展。因为这个项目的本身并不单单是封装Header,而是同样包括了下拉、下拉、分割线等操作。主要还是为了梳理并记录这么一种基于业务的思想。

这里封装的思想,直接延续正常的思路,也就是基于Type的对外封装。

首先,让我们的这个类去继承RecyclerView.Adapter,例如这样:HeaderRecyclerViewAdapter extends RecyclerView.Adapter。

然后在构造方法中传入我们正常的Adapter,然后创建俩个List<View>,用于放置我们的Header以及Footer。
对应的我们要对外暴露相应的add方法:

    public void addHeader(View headerView) {
        if (!headerViews.contains(headerView)) {
            headerViews.add(headerView);
            //当然这里可以使用notifyItemChanged(int position)去优化
            notifyDataSetChanged();
        }
    }

给Header设置一个合适的Type,比如:

public static final int VIEW_TYPE_HEADER_OFFSET = Integer.MIN_VALUE;

接下来就是getItemViewType()方法的处理了。

    @Override
    public int getItemViewType(int position) {
        if (!headerViews.isEmpty() && position < headerViews.size()) {
            return VIEW_TYPE_HEADER_OFFSET + position;
        }
        //计算我们常规数据的position,也就是减去Header的List的size的数量即可
        if (!headerViews.isEmpty()) {
            position -= headerViews.size();
        }
        //adapter就是我们在构造方法里传进来的RecyclerView.Adapter
        if (adapter != null && position < adapter.getItemCount()) {
            return adapter.getItemViewType(position);
        }     
    }

return了viewType,那么我们就要处理onCreateViewHolder()方法了。

    public static boolean isHeaderView(int viewType) {
        return viewType < VIEW_TYPE_HEADER_OFFSET;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (isHeaderView(viewType)) {
            return new HeaderViewHolder(headerViews.get(viewType - VIEW_TYPE_HEADER_OFFSET));
        }
        return adapter.onCreateViewHolder(parent, viewType);
    }

紧接着就是处理onBindViewHolder()方法。其实这里对于我们来说并不需要处理,因为我们来说,Header的操作应该交由外部处理,因此这里我们return掉,只需要对常规数据的adapter,调用super方法即可。

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int viewType = getItemViewType(position);
        //这里直接return的原因,我们这里不做对Header的处理,交由外部。
        if (isHeaderView(viewType)) {
            return;
        }
        position -= headerViews.size();
        adapter.onBindViewHolder(holder, position);
    }

到这我们的针对Header的封装就算是结束了,使用的时候也很简单,只需要在初始化了这个实现类后调用addHeader方法,传入我们自己的布局即可。(单纯只看这个封装,其实并不能给我们带来多么大的体验,但是如果综合多个功能去封装那所带来的战斗力将不言而喻)

Footer的话,和Header是同理的。


总结

其实单单是为了封装Header本身的意义并不大,就像上诉的列子,并无法给我们带来多么大的便利,但是如果我们综合去封装,就可以发现它存在的意义,比如:我的观公司RecyclerView封装有感中那样,把对应的功能封装到一起,那么作为一个业务的基类,我们将会发现可以很快捷完成不少类似的业务。

2018年7月2号,我正式开始了自己的Android工作,为了能够让自己能够好好完成工作,并且能够快速得到技术提升。所以打算以公众号的方式去敦促自己学习,我会把自己日常的学习笔记发布到公众号上,如果可以,共同进步!~


(周期计划-2)RecyclerView封装系列(1):Header、Footer
个人公众号
上一篇:Java基础学习随笔


下一篇:quartz调度框架在web中应用实例