步骤一:分析这个控件的组成
步骤二:回顾一下绘制和测量的相关知识
绘制文字:
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方法测量自己的高度和宽度
- 求出文字最长的宽度:
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;
}
复制代码
- 内边距
// 内边距可以通过自定义属性传入,同时在代码里写死一个默认的paddding。
private int mSegmentPaddingHorizontal = 16;
复制代码
- 整个文本的宽度
int maxWidth = 0;
int maxWidthItem = getMaxWidthOfTextArray(mTexts, paint);
maxWidth = (maxWidthItem + 2 * mSegmentPaddingHorizontal + 2 * mFrameWidth) * mTexts.length;
复制代码
- 根据父亲传入的规则进行调整自身宽度 下面是伪代码:
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;
复制代码
- 求出文本的高度
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;
}
复制代码
- 求出控件的高度
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;
复制代码
- 调用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