SegmentControlView模仿实现

步骤一:分析这个控件的组成

步骤二:回顾一下绘制和测量的相关知识

绘制文字:

canvas.drawText(text,x,y,paint)
复制代码

绘制边框:

Path path = new Path();
path.moveTo(left,top);
path.lineTo(right,top);
path.lineTo(right,bottom);
path.lineTo(left,bottom);
path.close();
canvas.drawPath(path,paint); //paint的样式设置成Stroke
复制代码

绘制圆弧:

//rect就是圆所在的矩形,startAngle是起始角度,sweepAngle是扫过的角度
path.arcTo(rect, startAngle,sweepAngle);
复制代码

如图:

绘制背景:

Path path = new Path();
path.moveTo(left,top);
path.lineTo(right,top);
path.lineTo(right,bottom);
path.lineTo(left,bottom);
path.close();
canvas.drawPath(path,paint); //paint的样式设置成FILL
复制代码

测量文字的宽度:

paintText = new Paint();
paintText.setAntiAlias(true);
paintText.setTextAlign(Paint.Align.CENTER); //这句话一定要上,否则测量不准确
paintText.setTextSize(mTextSize);
paint.measureText(text);
复制代码

测量文字的高度:

paintText = new Paint();
paintText.setAntiAlias(true);
paintText.setTextAlign(Paint.Align.CENTER); //这句话一定要上,否则测量不准确
Rect textBounds = new Rect();
paint.getTextBounds(text, 0, text.length(), textBounds);
textBounds.height();
复制代码

Paint类measureText与getTextBounds的区别: 答:measureText得到的结果要比getTextBounds得到的结果多一点,原因是,他们测量的区域不一样。 参考: Paint类measureText与getTextBounds的区别

获取文本的高度中点:

Math.abs(fontMetrics.top + fontMetrics.bottom)/2;
复制代码

步骤三:重写onMeasure方法测量自己的高度和宽度

  1. 求出文字最长的宽度:
private int getMaxWidthOfTextArray(String[] array, Paint paint){
        if(array == null){
            return 0;
        }
        int maxWidth = 0;
        for(String item : array){
            if(item != null){
                int itemWidth = getTextWidth(item, paint);
                maxWidth = Math.max(itemWidth, maxWidth);
            }
        }
        return maxWidth;
    }
     /**
     * 单个文字的宽度
     * @param text
     * @param paint
     * @return
     */
    private int getTextWidth(String text, Paint paint){
        if(!TextUtils.isEmpty(text)){
            return (int)(paint.measureText(text) + 0.5f);
        }
        return -1;
    }
复制代码
  1. 内边距
// 内边距可以通过自定义属性传入,同时在代码里写死一个默认的paddding。
private int mSegmentPaddingHorizontal =  16;
复制代码
  1. 整个文本的宽度
 int maxWidth = 0;
int maxWidthItem = getMaxWidthOfTextArray(mTexts, paint);
 maxWidth = (maxWidthItem + 2 * mSegmentPaddingHorizontal + 2 * mFrameWidth) * mTexts.length;
复制代码
  1. 根据父亲传入的规则进行调整自身宽度 下面是伪代码:
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){
    result = specSize;
}else{
  int maxTextWidth = 自己测量的文本的宽度;
 if(specMode == MeasureSpec.AT_MOST){
    result  = 取(specSize,maxTextWidth)数值较小的一个;
  }
 }
return result;
复制代码
  1. 求出文本的高度
 private int getMaxHeightOfTextArray(String[] array, Paint paint){
        if(array == null){
            return 0;
        }
        int maxHeight = 0;
        for(String item : array){
            if(item != null){
                int itemHeight = getTextHeight(item, paint);
                maxHeight = Math.max(itemHeight, maxHeight);
            }
        }
        return maxHeight;
    }

    private int getTextHeight(String text, Paint paint){
        if(!TextUtils.isEmpty(text)){
            Rect textBounds = new Rect();
            paint.getTextBounds(text, 0, text.length(), textBounds);
            return textBounds.height();
        }
        return -1;
    }
复制代码
  1. 求出控件的高度
    下面是伪代码:
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){
    result = specSize;
}else{
  int maxTextHeight = 自己测量的文本的高度;
 if(specMode == MeasureSpec.AT_MOST){
    result  = 取(specSize,maxTextHeight )数值较小的一个;
  }
 }
return result;
复制代码
  1. 调用setMeasuredDimension设置测量好的高度和宽度
 setMeasuredDimension(width, height);
复制代码

步骤四:onSizeChanged记录下测量好的宽高

//w,h分别是传递过来的宽,高
RectF rectF = new RectF(); 
rectF.left = getPaddingLeft();
rectF.top = getPaddingTop();
rectF.right = w - getPaddingRight();
rectF.bottom = h - getPaddingBottom();
复制代码

步骤五:开始进行绘制

这里可能有人会不知道如何绘制圆角,其实圆角可以看成是一个矩形,然后在矩形两个点间绘制圆弧,也就是步骤二中回顾的方法。

 for(int i = 0; i < mTexts.length; i++){
            float left = rectF.left + unitWidth * i;
            pathFrame.reset();
            if(i == 0){
                pathFrame.moveTo(rectF.left, rectF.top + mFrameCornerRadius);
                rectFArc.offsetTo(rectF.left, rectF.top);
                pathFrame.arcTo(rectFArc, 180, 90);
                pathFrame.lineTo(rectF.left + unitWidth, rectF.top);
                pathFrame.lineTo(rectF.left + unitWidth, rectF.bottom);
                pathFrame.lineTo(rectF.left + mFrameCornerRadius, rectF.bottom);
                rectFArc.offsetTo(rectF.left, rectF.bottom - 2 * mFrameCornerRadius);
                pathFrame.arcTo(rectFArc, 90, 90);
            }else if(i == (mTexts.length - 1)){
                pathFrame.moveTo(rectF.left + i * unitWidth, rectF.top);
                pathFrame.lineTo(rectF.right - mFrameCornerRadius, rectF.top);
                rectFArc.offsetTo(rectF.right - 2 * mFrameCornerRadius, rectF.top);
                pathFrame.arcTo(rectFArc, 270, 90);
                pathFrame.lineTo(rectF.right, rectF.bottom - mFrameCornerRadius);
                rectFArc.offsetTo(rectF.right - 2 * mFrameCornerRadius, rectF.bottom - 2 * mFrameCornerRadius);
                pathFrame.arcTo(rectFArc, 0, 90);
                pathFrame.lineTo(rectF.left + i * unitWidth, rectF.bottom);
            }else{
                pathFrame.moveTo(left, rectF.top);
                pathFrame.lineTo(left + unitWidth, rectF.top);
                pathFrame.lineTo(left + unitWidth, rectF.bottom);
                pathFrame.lineTo(left, rectF.bottom);
            }
            pathFrame.close();
 ....
           canvas.drawPath(pathFrame, paintBackground);
            canvas.drawPath(pathFrame, paintFrame);

            paintText.setColor(curTextColor);
            canvas.drawText(mTexts[i], left + unitWidth / 2,rectF.centerY() + textCenterYOffset, paintText);
}
复制代码

到这里,一个基本的SegmentControlView就算初步完成了。之后,只需要加入触摸事件,完成更改各种画笔的颜色就可以实现SegmentControlView的功能啦。

完整代码:segmentControlview

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值