BaseRecyclerViewAdapterHelper源码解读(五) header和footer完善

来源:互联网 发布:程序员三大浪漫 编辑:程序博客网 时间:2024/06/01 10:09

关于header和footer的完善

之前的源码阅读,添加header和footer,但是那个还不够完美,只能是在LinearLayoutManager时使用,在GridLayoutManager中使用会像普通item那样只占一个格子.

此篇文章为BaseRecyclerViewAdapterHelper源码解读第五篇,开源库地址,如果没有看过之前4篇文章的同学可以先去看看,大神可直接跳过.

BaseRecyclerViewAdapterHelper源码解读(一) 封装简单的adapter和万能的BaseViewHolder

BaseRecyclerViewAdapterHelper源码解读(二) 添加header和footer

BaseRecyclerViewAdapterHelper源码解读(三) 添加动画

BaseRecyclerViewAdapterHelper源码解读(四) 上拉加载更多

今天给大家带来BaseRecyclerViewAdapterHelper是关于header和footer完善的主要解决了StaggeredGridLayoutManager和GridLayoutManager在添加header和footer时不跨格子的问题.由于本人才学尚浅,如有有不对的地方,欢迎指正,谢谢.

设置header和footer宽度为跨区域

/**     * Called when a view created by this adapter has been attached to a window.     * simple to solve item will layout using all     *     * @param holder     */    @Override    public void onViewAttachedToWindow(K holder) {        super.onViewAttachedToWindow(holder);        int type = holder.getItemViewType();        if (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type ==                LOADING_VIEW) {            //设置为跨区域  比如是StaggeredGridLayoutManager时,header或者footer等应该如何展示            setFullSpan(holder);        } else {            //添加动画到holder的itemView上,并执行动画            addAnimation(holder);        }    }    /**     * When set to true, the item will layout using all span area. That means, if orientation     * is vertical, the view will have full width; if orientation is horizontal, the view will     * have full height.     * if the hold view use StaggeredGridLayoutManager they should using all span area     *     * @param holder True if this item should traverse all spans.     */    protected void setFullSpan(RecyclerView.ViewHolder holder) {        if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {            StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager                    .LayoutParams) holder                    .itemView.getLayoutParams();            params.setFullSpan(true);        }    }

当一个view被创建并添加到window时会调用onViewAttachedToWindow()方法,而在此时,我们做一下判断,
如果是空布局,header,footer,加载中布局,那么将该item设置为跨区域.
这样就不会出现在StaggeredGridLayoutManager中上面的那些布局是只占了一个跨度.说不清楚,看图

如果不这样做,那么可能图中的那里标注出来的可能就会是header的效果,而不是跨2格.

设置每个item所占跨度

    /**     * if asFlow is true, footer/header will arrange like normal item view.     * only works when use {@link GridLayoutManager},and it will ignore span size.     * 如果asFlow是true的话,那么footer和header就像正常item一样,不会跨区域     * 默认是false,header和footer都占满屏幕     */    private boolean headerViewAsFlow, footerViewAsFlow;    /**     * 设置header不跨区域  就像正常item     * true:不跨区域   false:跨区域     */    public void setHeaderViewAsFlow(boolean headerViewAsFlow) {        this.headerViewAsFlow = headerViewAsFlow;    }    public boolean isHeaderViewAsFlow() {        return headerViewAsFlow;    }    /**     * 设置footer不跨区域  就像正常item     * true:不跨区域   false:跨区域     */    public void setFooterViewAsFlow(boolean footerViewAsFlow) {        this.footerViewAsFlow = footerViewAsFlow;    }    public boolean isFooterViewAsFlow() {        return footerViewAsFlow;    }    /**     * 判断当前type是否是特殊的type     */    protected boolean isFixedViewType(int type) {        return type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type ==                LOADING_VIEW;    }    /**     * RecyclerView在开始观察该适配器时调用。     * 请记住,多个RecyclerView可能会观察到相同的适配器。     * <p>     * Adapter与RecyclerView关联起来     * 这里面主要是做表格布局管理器的头布局和脚布局自占一行的适配     *     * @param recyclerView     */    @Override    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {        super.onAttachedToRecyclerView(recyclerView);        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();        if (manager instanceof GridLayoutManager) {            final GridLayoutManager gridManager = ((GridLayoutManager) manager);            //设置adapter中每个Item所占用的跨度数            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {                @Override                public int getSpanSize(int position) {                    int type = getItemViewType(position);                    //如果当前type为header并且asFlow为true:那么header设置为不跨区域,就和正常item一样.只占1格                    if (type == HEADER_VIEW && isHeaderViewAsFlow()) {                        return 1;                    }                    if (type == FOOTER_VIEW && isFooterViewAsFlow()) {                        return 1;                    }                    //如果用户没有自定义SpanSizeLookup  SpanSizeLookup是用来查询每个item占用的跨度数的实例                    if (mSpanSizeLookup == null) {                        /*                        1.如果是特殊的type,那么item的跨度设置为当前gridManager的SpanCount                        即如果我设置的是mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));                        那么当前item占2格的跨度                        2.如果item不是特殊的item,那么就是占1个格子,不跨                        */                        return isFixedViewType(type) ? gridManager.getSpanCount() : 1;                    } else {                        /*                        1.如果是特殊的type,那么item的跨度设置为当前gridManager的SpanCount                        即如果我设置的是mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));                        那么当前item占2格的跨度                        2.如果item不是特殊的item,那么交给外部调用者来处理每个item应该占多少个格                        */                        return isFixedViewType(type) ? gridManager.getSpanCount() :                                mSpanSizeLookup.getSpanSize(gridManager,                                        position - getHeaderLayoutCount());                    }                }            });        }    }    private SpanSizeLookup mSpanSizeLookup;    /**    用于外部调用者设置每个item的跨度,除了header,footer,emptyView,loadMoreView    */    public interface SpanSizeLookup {        int getSpanSize(GridLayoutManager gridLayoutManager, int position);    }    /**     * @param spanSizeLookup instance to be used to query number of spans occupied by each item     用于查询每个item占用的跨度数的实例        */    public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {        this.mSpanSizeLookup = spanSizeLookup;    }

首先,我们将empty_view,header_view,footer_view,loading_view这些特殊的布局,单独写个方法去判断是否属于特殊的布局.
并且我们设置了2个属性,用于开发者可能会将布局设置为header(footer)和普通item一样都只占1个格子,而不是占满屏幕(占gridManager的SpanCount个格子).
在RecyclerView在开始观察该适配器时调用onAttachedToRecyclerView()方法,然后在里面判断是否是GridLayoutManager,如果是则
设置adapter中每个Item所占用的跨度数.

getSpanSize()逻辑:
- 如果当前type为header或footer并且asFlow为true:那么header或footer设置为不跨区域,就和正常item一样.只占1格.
- 如果用户没有自定义SpanSizeLookup SpanSizeLookup是用来查询每个item占用的跨度数的实例
- 1.如果是特殊的type,那么item的跨度设置为当前gridManager的SpanCount
即如果我设置的是mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));
那么当前item占2格的跨度
2.如果item不是特殊的item,那么就是占1个格子,不跨
- 1.如果是特殊的type,那么item的跨度设置为当前gridManager的SpanCount
即如果我设置的是mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));
那么当前item占2格的跨度
2.如果item不是特殊的item,那么交给外部调用者来处理每个item应该占多少个格

总结

  • 如果是StaggeredGridLayoutManager,那么在onViewAttachedToWindow()时设置它(header,footer)的跨度为整个SpanCount
  • 如果是GridLayoutManager,那么在onAttachedToRecyclerView()时设置它(header,footer)的跨度为整个SpanCount

其实就是判断一个item是否是header或者footer,如果是则设置它的跨度为SpanCount.eg:如果是GridLayoutManager,纵向,那么header就占满整个屏幕;
当然也支持把header和footer设置为普通item跨度的.