美文网首页安卓实用知识Android UI技术
绘制RecyclerView的悬浮分隔线

绘制RecyclerView的悬浮分隔线

作者: CSU_IceLee | 来源:发表于2017-03-18 15:14 被阅读267次

    悬浮ItemDecoration

    先来一个效果图:

    悬浮效果图
    如果你没有用过RecyclerView的分隔线,那么可以看看我的前篇文章:
    绘制RecyclerView的分隔线

    思路步骤

    1. 设置回调接口,用于判断每个item的类型,以及各个类型对应的名称
    public interface DecorationListener{
        int getType(int position);//获得分组的type
        String getTypeText(int position);//获得该组的文字信息
    }
    
    1. 继承RecyclerView.ItemDecoration重写getItemOffsets将需要绘制分隔栏的地方设置偏移量,我们这里是采用分组的形式,即不同组之间才需要绘制分隔符。可以看看我的图片↑。
    2. 重写onDraw
      在设置了偏移量的地方绘制分隔栏
    3. 重写onDrawOver
      在头顶绘制悬浮分隔栏

    调用顺序

    首先会调用getItemOffsets,然后是onDraw,然后会绘制item本身,最后时onDrawOver,所以onDrawOver里面绘制的会覆盖掉你的view,所以这就是现实了悬浮在view之上!

    代码实现

    1. 定义好要用的变量
    private Context mContext;
    private int mTitleHeight;//分隔栏高度
    private Paint mPaint;//画背景的画笔
    private Paint mTextPaint;//画文字的画笔
    private Rect mBounds;//文字边界
    private int mTextSize;
    private int mTextColor;
    private int mBackgroundColor;
    //用于得到type和type对应的Text
    private DecorationListener mListener;
    
    1. 初始化变量
        public FloatingDecoration(Context context,DecorationListener listener){
            mContext = context;
            mTitleHeight = 100;
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setColor(Color.GRAY);
            mBounds = new Rect();
            mListener = listener;
    
            mTextPaint = new Paint();
            mTextPaint.setColor(Color.WHITE);
            mTextPaint.setAntiAlias(true);//抗锯齿
            mTextPaint.setTextSize(sp2px(context,30));
    
        }
    
    1. 设置偏移量
     @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            int position =  parent.getChildLayoutPosition(view);
                if (position == 0) {//第0个肯定是新的一组
                    outRect.set(0, mTitleHeight, 0, 0);
                } else {//其他的通过判断
                    if (mListener.getType(position)!= mListener.getType(position-1)) {//通过接口来获得类型
                        outRect.set(0, mTitleHeight, 0, 0);//跟前一个tag不一样了,说明是新的分类,也要title
                    } else {
                        outRect.set(0, 0, 0, 0);
                    }
                }
        } 
    
    1. 绘制分隔栏
     @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            final int left = parent.getPaddingLeft();
            final int right = parent.getWidth() - parent.getPaddingRight();
            final int childCount = parent.getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                        .getLayoutParams();
                int position = params.getViewLayoutPosition();
                    if (position == 0) {//等于0肯定要有分组title的
                        drawTitleArea(c, left, right, child, params, position);
                    }else {//其他的通过判断
                        if (mListener.getType(position)!= mListener.getType(position-1)) {
                            //且跟前一个tag不一样了,说明是新的分组,也要title
                            drawTitleArea(c, left, right, child, params, position);
                        }
                    }
            }
        }
    
    1. 绘制文字区域
     private void drawTitleArea(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) {//最先调用,绘制在最下层
            c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint);
            //绘制文字
            mTextPaint.getTextBounds(mListener.getTypeText(position), 0,mListener.getTypeText(position).length(), mBounds);
            Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
            //文字baseline计算方法: 矩形区域的top + bottom - fontMetrics.bottom - fontMetrics.top 除以 2
            int baseline = ( child.getTop() - mTitleHeight + child.getTop() - fontMetrics.bottom - fontMetrics.top) / 2;
            c.drawText(mListener.getTypeText(position), child.getPaddingLeft(), baseline, mTextPaint);
        } 
    

    其实目前已经可以实现分组了!但是还没有悬浮的效果。

    1. 绘制顶部的悬浮分隔栏
      来看一张原理图:


      原理图

      好,让我们来开始编码:

    @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {//最后调用 绘制在最上层
            //获得可见的第一个view的position
            int pos = ((LinearLayoutManager)(parent.getLayoutManager())).findFirstVisibleItemPosition();`
            //要绘制的文字
            String tag = mListener.getTypeText(pos);
            //获得在屏幕上能看到的第一个view
            View child = parent.getChildAt(0);
    
            if(mListener.getType(pos)!= mListener.getType(pos+1)){
                //这是最后一个
                int bottom = child.getBottom();
                if(bottom<mTitleHeight){
                    //底部小于分隔栏的高度了  说明已经上去了
                    //绘制背景  随着最后一个的bottom一起向上移动 
                    //为了简洁我就没写各边的padding了
                    c.drawRect(0, 0, parent.getRight(), bottom, mPaint);
                    mPaint.getTextBounds(tag, 0, tag.length(), mBounds);
    
                    //文字
                    Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
                    int baseline = (bottom + bottom -mTitleHeight - fontMetrics.bottom - fontMetrics.top) / 2;
                    c.drawText(tag,child.getPaddingLeft(),baseline,mTextPaint);
                    return;
                }
            }
            //如果不是最后一个 那就直接绘制在RecyclerView的顶部!
            //分隔线的背景
            c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint);
    
            //分组的文字
            mTextPaint.getTextBounds(tag, 0, tag.length(), mBounds);
            Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
            int baseline = (mTitleHeight - fontMetrics.bottom - fontMetrics.top) / 2;
            c.drawText(tag, child.getPaddingLeft(),baseline,   mTextPaint);
       }
    

    结语:其实不绘制文字,还是很容易就搞定这个的,但是要绘制文字就要做许多计算!主要就是baseline(基线)的计算,文字是绘制在基线上边的。大家可以多看看文字绘制的方法,最好动手画画,就能理解了。

    相关文章

      网友评论

      • Fi7z:能给个源码地址吗
        CSU_IceLee:额。。不好意思哈,那个项目现在已经变得混乱不堪了,这个源文件可以看看这个http://ice97.cn:8080/Image/FloatingDecoration.java 跟着文章一步一步来是很容易就做出来的

      本文标题:绘制RecyclerView的悬浮分隔线

      本文链接:https://www.haomeiwen.com/subject/jzbunttx.html