RecyclerView ItemDecoration的使用

RecyclerView真的很强大,真心的,这一点毋庸置疑,现在公司的项目中有用到,趁着项目刚上线,空余时间总结一下项目中的知识点。
首先项目中需求如下图(有改动):

这里写图片描述

消息分为:系统消息和活动消息,服务端返回一个列表,根据列表将相邻的Item分别归类,相同类型Item的同一个标题,不相同的Item,就多一个标题。如上图所示,系统消息在一块,紧接着是活动消息,再紧接着时系统消….。

其实这个需求有一种比较普通的做法,那就是两个RecyclerView嵌套,这样省事省力,这样做的话,等于牺牲了效率,节省了力气~。

这是RecyclerView的ItemDecoration功能就应运而生了。

*

简单功能

*
首先说下ItemDecoration的简单功能。实现如下功能:
这里写图片描述

没错,就是分割线,使用ItemDecoration实现分割线。我们知道自定义ViewGroup的步骤是先测量View,然后给绘制View,同理RecyclerView也不例外,先给空间,再绘制。
代码如下:

    /**
     * 给空间
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        if(parent.getChildAdapterPosition(view) != 0) {
            outRect.top = 1;
            dividerHeight = 1;
        }
    }

    /**
     * 绘制
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        int childCount = parent.getChildCount();
        for(int i = 0; i < childCount; i++) {
            View view = parent.getChildAt(i);

            int viewLayoutPosition = ((RecyclerView.LayoutParams) (view.getLayoutParams())).getViewLayoutPosition();

            if(viewLayoutPosition == 0) {
                continue;
            }
            float dividerTop = view.getTop() - dividerHeight;
            float dividerLeft = parent.getPaddingLeft();
            float dividerRight = parent.getWidth() - parent.getPaddingRight();
            float dividerBottom = view.getTop();

            c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);

        }
    }

新建一个类继承自ItemDecoration,然后重写getItemOffsets方法,该方法就是给分割线所需要的空间,线高:dividerHeight 。至于outRect,看下图:

这里写图片描述

从图中可以看出,这个内层的矩形代表View,外层的代表outRect,我们给outRect.top = 1,也就是给了分割线的空间,每一个View都有分割线的空间(除了Position = 0)。

空间有了,接下来就是绘制了,也就是onDraw方法,方法的参数含有canvas,就可以知道,我们可以尽情的绘制我们想要绘制的view了。先遍历所有的View, 然后通过view可以得到该view此时此刻再recyclerView中的真实position,通过该position就可以做我们想做的事情了,接下来就是自定义View的东西了。在此不再赘述。

实现文章开头的最开始的需求

上面的看懂了,下面的我相信不会太难的,也就是多了几个if语句而已。
首先开始给空间,空间分两种:平常的间隔和包含标签的间隔(系统消息,活动消息)。通过参数中的view,可以获得该view再recyclerView中的位置position,如果position == 0,表示第一个,那肯定得是包含标签的了,然后就是position != 0 , 此时我们通过position,获得数据源list中的type(活动消息,系统消息)判断,如果前一个preType.equal(currentType),那么就是普通的间隔,反之,就是包含标签的间隔。
先贴上变量的声明:

    private Paint mPaint;

    private int spaceHeight; //间隔高度(无标签)

    private int spaceTotalHeight = 0; //间隔高度(标签)

    private int dateBackHeight = 0; //时间日期背景高度

    private int dateToBackWidth = 0; //日期背景宽度

    private int testSize = 0; //字体大小

    private int color_date_back = Color.parseColor("#999999"); //日期背景色

    private int color_text = Color.parseColor("#333333"); //字体颜色

    private List<DateModel> dateModelList;

代码:

   @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        int position = ((RecyclerView.LayoutParams)view.getLayoutParams()).getViewLayoutPosition();

        if(position > -1) {
            if(position == 0) {
                outRect.set(0, spaceTotalHeight, 0, 0);
            } else {
                String preType = dateModelList.get(position - 1).type;
                String currentType = dateModelList.get(position).type;

                if(preType.equals(currentType)) { //相同类型
                    outRect.set(0, spaceHeight, 0, 0);
                } else { //不同类型
                    outRect.set(0, spaceTotalHeight, 0, 0);
                }
            }
        }
    }

然后就是绘制了,其实绘制一样的呢,跟上面的逻辑一样的,直接上代码吧:


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        int childCount = parent.getChildCount();
        for(int i = 0; i < childCount; i++) {
            View childView = parent.getChildAt(i);
            int viewLayoutPosition = ((RecyclerView.LayoutParams) childView.getLayoutParams()).getViewLayoutPosition();
            String type = dateModelList.get(viewLayoutPosition).type;
            if(viewLayoutPosition == 0) {
                onTextData(c, type, childView, parent);
            } else {
                String preType = dateModelList.get(viewLayoutPosition - 1).type;
                if(preType.equals(type)) {
                    //do nothing
                } else {
                    onTextData(c, type, childView, parent);
                }
            }
        }
    }

    private void onTextData(Canvas c, String type, View childView, RecyclerView parent) {
        float measureText = mPaint.measureText(type);
        //画矩形(日期背景)
        float rectBackLeft = parent.getWidth() / 2 - measureText / 2 - dateToBackWidth;
        float rectBackRight = parent.getWidth() / 2 + measureText / 2 + dateToBackWidth;
        float rectBackTop = childView.getTop() - spaceTotalHeight / 2 - dateBackHeight / 2;
        float rectBackBottom = childView.getTop() - (spaceTotalHeight / 2 - dateBackHeight / 2);

        mPaint.setColor(color_date_back);
        c.drawRect(rectBackLeft, rectBackTop, rectBackRight, rectBackBottom, mPaint);

        //画字(日期)
        float dateLeft = parent.getWidth() / 2 - measureText / 2 - dateToBackWidth + dateToBackWidth;
        Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        //(fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent; //baseLine
        float baseLine = childView.getTop() - spaceTotalHeight / 2 + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent;

        mPaint.setColor(color_text);
        c.drawText(type, dateLeft, baseLine, mPaint);
    }

其实感觉RecyclerView将跟多的功能实现都交给了开发者,开发者根据自己的需求,去定制属于自己的代码。很强大~

Demo下载地址:https://download.csdn.net/upload/success

猜你喜欢

转载自blog.csdn.net/lmq121210/article/details/80861781