RecyclerView 之ItemDecoration悬浮分隔线

RecyclerView 之ItemDecoration分隔线
RecyclerView 之ItemDecoration悬浮分隔线
RecyclerView 之ItemDecoration分隔线+悬浮分隔线


上一节分析了RecyclerView 之ItemDecoration分隔线
今天来一起撸RecyclerView的悬浮分隔。

RecyclerView 之ItemDecoration分隔线中有说到ItemDecoration三大方法的getItemOffsetsonDraw,那么这一节就该 onDrawOver大显身手了!

void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
同样是绘制内容,但与onDraw() 的区别是:绘制在图层的最上层,会覆盖底层的视图

如下所示:

public class RecyclerItemDecoration2 extends RecyclerView.ItemDecoration {

    /**
     * 悬浮文案 颜色、字体颜色、字体大小、左Margin
     */
    private Paint mStickPaint;
    private int mStickColor = Color.parseColor("#00cd00");

    public RecyclerItemDecoration2() {
        //悬浮分隔
        mStickPaint = new Paint();
        mStickPaint.setAntiAlias(true);
        mStickPaint.setDither(true);
        mStickPaint.setColor(mStickColor);
    }

    /**
     * 同样是绘制内容,但与onDraw() 的区别是:绘制在图层的最上层,会覆盖底层的视图
     *
     * @param c      Canvas
     * @param parent RecyclerView
     * @param state  RecyclerView.State
     */
    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        //parent.getChildCount为当前页面中可见的Child view的个数
        for (int i = 0; i < parent.getChildCount(); i++) {
            View childView = parent.getChildAt(i);
            c.drawRect(0, childView.getTop(), parent.getRight(), childView.getTop() + 50, mStickPaint);
        }
    }

}
分隔线在ItemView上层

因此,可以通过getItemOffsetsonDrawOver配合实现悬浮分隔:
先附上完整代码抛个砖(使用的RecyclerView有下拉刷新和上拉加载,故mOffsetStart、mOffsetEnd都设置为1):

package com.test.wrapper.indexList;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.View;

public class RecyclerItemDecoration extends RecyclerView.ItemDecoration {

    /**
     * 悬浮文案 颜色、字体颜色、字体大小、左Margin
     */
    private Paint mStickPaint;
    private int mStickColor = Color.parseColor("#dbdbdb");
    private int mStickTextColor = Color.parseColor("#333333");
    private float mStickTextSize = 40f;
    private int mStickTextLeftMargin = 100;
    private Paint mStickTextPaint;
    /**
     * 悬浮文案 文案区域框
     */
    private Rect mStickTextBounds;
    /**
     * 悬浮文案 高度
     */
    private int mStickHeight = 100;
    /**
     * RecyclerView 首部偏移量,前mOffsetStart个item不展示分隔线
     */
    private int mOffsetStart = 1;
    /**
     * RecyclerView 尾部偏移量,后mOffsetEnd个item不展示分隔线
     */
    private int mOffsetEnd = 1;
    /**
     * 悬浮文案缓存
     */
    private SparseArray mStickText = new SparseArray<>();

    public RecyclerItemDecoration() {

        //悬浮分隔
        mStickPaint = new Paint();
        mStickPaint.setAntiAlias(true);
        mStickPaint.setDither(true);
        mStickPaint.setColor(mStickColor);

        //悬浮分隔文字
        mStickTextPaint = new TextPaint();
        mStickTextPaint.setAntiAlias(true);
        mStickTextPaint.setDither(true);
        //文字加粗
        mStickTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mStickTextPaint.setColor(mStickTextColor);
        mStickTextPaint.setTextSize(mStickTextSize);
        //悬浮分隔区域
        mStickTextBounds = new Rect();
    }

    /**
     * 用以设置ItemView的内嵌偏移长度(inset)
     *
     * @param outRect Rect
     * @param view    View
     * @param parent  RecyclerView
     * @param state   RecyclerView.State
     */
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        //获取子View在RecyclerView中位置
        int positionInRecycler = parent.getChildAdapterPosition(view);
        if (isHasStickDivider(parent, positionInRecycler)) {
            outRect.top = mStickHeight;
        }
    }

    /**
     * 同样是绘制内容,但与onDraw() 的区别是:绘制在图层的最上层,会覆盖底层的视图
     *
     * @param c      Canvas
     * @param parent RecyclerView
     * @param state  RecyclerView.State
     */
    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        //如果子ItemView数量小于偏移数量,则返回
        if (parent.getChildCount() <= mOffsetStart + mOffsetEnd) {
            return;
        }
        //当前页面中的悬浮分隔的计数器
        int stickIndex = 0;
        //parent.getChildCount为当前页面中可见的Child view的个数
        for (int i = 0; i < parent.getChildCount(); i++) {
            //获取页面中View在RecyclerView中的位置
            final View childView = parent.getChildAt(i);
            int positionInRecycler = parent.getChildAdapterPosition(childView);
            //悬浮分隔文案:若为空,则不展示
            String stickText = getStickText(positionInRecycler, parent);
            if (!TextUtils.isEmpty(stickText)) {
                if (childView.getTop() <= mStickHeight) {
                    //若有悬浮分隔的childView顶部 <= 悬浮分隔的高度, 则悬浮顶部
                    drawStickText(c, 0, 0, parent.getRight(), mStickHeight, stickText);
                } else {
                    //若有悬浮分隔的childView顶部 > 悬浮分隔的高度, 则绘制该View的悬浮分隔线
                    drawStickText(c, 0, childView.getTop() - mStickHeight, parent.getRight(), childView.getTop(), stickText);
                    //如果是该页面的第一个悬浮分割线,则寻找是否有上一个悬浮分隔
                    drawOverLastStick(c, parent, childView, positionInRecycler, stickIndex, mStickHeight);
                }
                stickIndex++;
            }
        }
        //若当前页面中没有ChildView拥有悬浮分隔线,则寻找上一个悬浮分隔线,并悬浮顶部
        if (stickIndex == 0) {
            int positionInRecycler = parent.getChildAdapterPosition(parent.getChildAt(0));
            //上一个悬浮文案
            String lastStickText = getLastStickText(positionInRecycler, parent);
            if (!TextUtils.isEmpty(lastStickText)) {
                drawStickText(c, 0, 0, parent.getRight(), mStickHeight, lastStickText);
            }
        }
    }

    private void drawOverLastStick(Canvas c, RecyclerView parent, View childView, int positionInRecycler, int stickIndex, int currentStickHeight) {
        //如果是该页面的第一个悬浮分割线,则寻找是否有上一个悬浮分隔,若有,则上一个悬浮分隔正在顶部悬浮
        if (stickIndex == 0) {
            //若有上一个悬浮分隔,则悬浮顶部
            String lastStickText = getLastStickText(positionInRecycler, parent);
            if (!TextUtils.isEmpty(lastStickText)) {
                //若该页面的第一个悬浮分割线的底部 < 悬浮分隔高度的2倍(悬浮顶部 + 该childView的悬浮分隔,则变换悬浮顶部的位置,实现向上推动的效果
                if (childView.getTop() < mStickHeight + currentStickHeight) {
                    drawStickText(c, 0,
                            childView.getTop() - mStickHeight - currentStickHeight,
                            parent.getRight(),
                            childView.getTop() - currentStickHeight, lastStickText);
                } else {
                    drawStickText(c, 0, 0, parent.getRight(), mStickHeight, lastStickText);
                }
            }
        }
    }

    //某个ItemView是否有悬浮分隔线
    private boolean isHasStickDivider(RecyclerView parent, View childView) {
        int position = parent.getChildAdapterPosition(childView);
        return !TextUtils.isEmpty(getStickText(position, parent));
    }

    /**
     * 某位置是否有悬浮分隔
     * 子类可覆盖
     *
     * @param parent             RecyclerView
     * @param positionInRecycler RecyclerView中位置
     * @return boolean
     */
    protected boolean isHasStickDivider(RecyclerView parent, int positionInRecycler) {
        return !TextUtils.isEmpty(getStickText(positionInRecycler, parent));
    }

    //悬浮分隔文案
    protected String getStickText(int positionInRecycler, @NonNull RecyclerView parent) {
        String stickText = mStickText.get(positionInRecycler);
        if (TextUtils.isEmpty(stickText)) {
            stickText = onCreateStickText(positionInRecycler, parent);
        }
        return stickText;
    }

    //绘制悬浮分隔以及文案
    private void drawStickText(Canvas c, float left, float top, float right, float bottom, String text) {
        c.drawRect(left, top, right, bottom, mStickPaint);
        mStickTextPaint.getTextBounds(text, 0, text.length(), mStickTextBounds);
        c.drawText(text, left + mStickTextLeftMargin, bottom - mStickHeight / 2f + mStickTextBounds.height() / 2f, mStickTextPaint);
    }


    //获取某位置的上一个悬浮分隔文案
    private String getLastStickText(int positionInRecycler, RecyclerView parent) {
        String lastStickText = "";
        for (int i = positionInRecycler - 1; i >= 0; i--) {
            lastStickText = getStickText(i, parent);
            if (!TextUtils.isEmpty(lastStickText)) {
                break;
            }
        }
        return lastStickText;
    }

    /**
     * 悬浮分隔文案
     * 子类可覆盖
     *
     * @param positionInRecycler RecyclerView中位置
     * @param parent             RecyclerView
     * @return String
     */
    protected String onCreateStickText(int positionInRecycler, @NonNull RecyclerView parent) {
        return null;
    }

    //绘制悬浮分隔以及View
    private void drawStickView(Canvas c, float left, float top, float right, float bottom, View stickView) {
        if (stickView.getDrawingCache() != null) {
            c.drawBitmap(stickView.getDrawingCache(), left, top, null);
        }
    }

}

RecyclerView中使用(使用的RecyclerView有下拉刷新,相当于mOffsetStart = 1,故 (positionInRecycler - 1) ):

        mRecyclerView.addItemDecoration(object : RecyclerItemDecoration() {
            override fun onCreateStickText(positionInRecycler: Int, parent: RecyclerView): String? {
                return if ((positionInRecycler - 1) % 3 == 0) "$positionInRecycler" else null
            }
        })
下拉刷新时
顶部顶上去的效果

至此,已实现了悬浮分隔。
那么,RecyclerView分隔线和悬浮分隔线一起使用呢?
RecyclerView 之ItemDecoration分隔线+悬浮分隔线

欢迎大家多多留言交流哈 ·_·

你可能感兴趣的:(RecyclerView 之ItemDecoration悬浮分隔线)