首先看下效果图
在做这个项目之前先了解下文字获取
我之前也写过一篇文章是自定义view——自定义圆环进度条:http://blog.csdn.net/qq_24675479/article/details/78880078
今天详细讲解一下baseLine 基线(参考文章:文淑大神的自定义View之绘图篇(四)http://blog.csdn.net/u012551350/article/details/51361778)
FontMetrics
top:可绘制的最高高度所在线
bottom:可绘制的最低高度所在线
ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
获取实例
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
Paint.FontMetricsInt fm= mPaint.getFontMetricsInt();
成员变量
float ascent = fontMetrics.ascent;
float descent = fontMetrics.descent;
float top = fontMetrics.top;
float bottom = fontMetrics.bottom;
float leading = fontMetrics.leading;
这里的ascent,descent,top,bottom,leading指的是到基线baseline的位置
文字的高度
float top = fontMetrics.top + baseLineY;
float bottom = fontMetrics.bottom + baseLineY;
//文字高度
float height= bottom - top; //注意top为负数
//文字中点y坐标
float center = (bottom - top) / 2;
即中线是(bottom-top)/2,实际呢是bottom+top只是因为这里的top是负的(图片上的top到baseline的距离)
已知中线求baseline
结论
baseline=centerY+A-fm.bottom;
centerY呢实际就是getHeight/2,整体高度的一半,然后求基线的y坐标,实际就是(top-bottom)/2-fontMetrics.bottom;再次强调:这里的ascent,descent,top,bottom,leading指的是到基线baseline的位置。最后相加getHeight+(top-bottom)/2-fontMetrics.bottom**
接下来开始做项目:
- 第一步自定义属性
一共需要:内部圆环颜色,外部圆环颜色,圆环宽度,文字颜色,文字大小五种
<declare-styleable name="QQStepView">
<attr name="outerColor" format="color" />
<attr name="innerColor" format="color" />
<attr name="borderWidth" format="dimension" />
<attr name="stepTextSize" format="dimension" />
<attr name="stepTextColor" format="color" />
</declare-styleable>
- 自定义view获取属性,设置属性等
public class QQStepView extends View {
private int mOuterColor = Color.RED;
private int mInnerColor = Color.BLUE;
private int mBorderWidth = 20;
private int mStepTextSize;
private int mStepTextColor;
private Paint mOuterPaint, mInnerPaint, mTextPaint;
private int mStepMax;//总的步数
private int mCurrentStep;//当前步数
public QQStepView(Context context) {
this(context, null);
}
public QQStepView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public QQStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
mOuterColor = ta.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
mInnerColor = ta.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
mBorderWidth = (int) ta.getDimension(R.styleable.QQStepView_borderWidth, mBorderWidth);
mStepTextSize = ta.getDimensionPixelOffset(R.styleable.QQStepView_stepTextSize, mStepTextSize);
mStepTextColor = ta.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
ta.recycle();
//内弧
mOuterPaint = new Paint();
mOuterPaint.setAntiAlias(true);
mOuterPaint.setStrokeWidth(mBorderWidth);
mOuterPaint.setColor(mOuterColor);
mOuterPaint.setStrokeCap(Paint.Cap.ROUND);//设置下方为圆形
mOuterPaint.setStyle(Paint.Style.STROKE);//设置内部为空心
//外弧
mInnerPaint = new Paint();
mInnerPaint.setAntiAlias(true);
mInnerPaint.setStrokeWidth(mBorderWidth);
mInnerPaint.setColor(mInnerColor);
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);//设置下方为圆形
mInnerPaint.setStyle(Paint.Style.STROKE);//设置内部为空心
//文字
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(mStepTextSize);
mTextPaint.setColor(mStepTextColor);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//调用者在布局文件中可能 wrap_content导致宽高不一致
//确保是正方形
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width > height ? height : width, width > height ? height : width);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//1.画外圆弧 边缘没有显示完整
//RectF recf = new RectF(0, 0, getWidth(), getHeight());
int center = getWidth() / 2;
int radius = getWidth() / 2 - mBorderWidth / 2;
//mBorderWidth/2,mBorderWidth/2,getWidth()-mBorderWidth/2,getWidth()-mBorderWidth/2
RectF recf = new RectF(center - radius,
center - radius,
center + radius,
center + radius);
canvas.drawArc(recf, 135, 270, false, mOuterPaint);
//2.绘制内圆弧
float sweepAngle = (float) mCurrentStep / mStepMax;
canvas.drawArc(recf, 135, sweepAngle * 270, false, mInnerPaint);
//3.绘制文字
String stepText = mCurrentStep + "";
Rect rect = new Rect();
mTextPaint.getTextBounds(stepText,0,stepText.length(),rect);
int dx = getWidth() / 2 - rect.width() / 2;
//第一种方式获取高度
//int dy = getWidth() / 2 + rect.width()/2;
//第二种表达方式获取高度
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
//获取中心(fontMetrics.bottom - fontMetrics.top) / 2
int dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(stepText, dx, baseLine, mTextPaint);
}
//其他,写几个方法让他动起来
public void setStepMax(int mStepMax) {
this.mStepMax = mStepMax;
}
public void setCurrentStep(int mCurrentStep) {
this.mCurrentStep = mCurrentStep;
//不断绘制 onDraw()
invalidate();
}
}
在MainActivity中设置动画
final QQStepView qqStepView = (QQStepView) findViewById(R.id.step_view);
qqStepView.setStepMax(5000);
//属性动画
ValueAnimator animator = ValueAnimator.ofFloat(0, 3000);//0到3000的变化
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentStep = (float) animation.getAnimatedValue();//获取当前值
qqStepView.setCurrentStep((int) currentStep);
}
});
animator.setDuration(2000);
animator.start();