可能以前平时对绘制文字要求不高,没怎么关注过文字基线的文字,只是知道有这么个概念.上一篇文章中有一个绘制简单进度条的例子,里边就有居中绘制文字的代码。
代码是这样的:
float baseline = getHeight() * 2 / 3;
为什么我这样算呢?
因为文字的绘制,在Y轴来说是从基线开始的,基线就是四线格中的第三条线,它的位置刚好就是四线格的2/3的位置。所以这里就用 view的高度*2/3了。
这里因为是凑巧View的高度不是太高,所以并没有看出什么问题。想象一下如果View的高度是屏幕的高度,我还用View的高度的2/3位置作为基线还行么?
答案当然是不行了,基线的位置并不是单纯的从所在View的高度上决定的,而是根据文字的FontMetrics和View的中线确定的。
更加详细的解释请看drawText详解,这篇文章写得很清楚,而且也有推倒过程.
下边的例子就是使用大神文章中的方法计算的baseline做的一个环形进度条,加了自定义属性,可以在xml中设置文字的大小、文字颜色,进度条背景,进度条颜色,进度条宽度等。代码中有详细的注释。
图中的圆环进度条就是这次的代码效果
环形进度条思路:
1. 自定义属性
-在attrs文件中声明需要的属性
绘制背景圆环
**-在onSizeChanged()方法中根据View的宽高计算中心点坐标和半径。
-注意:这个圆环是View的内切圆,计算半径需要减去圆环的宽度**
绘制当前进度圆弧
**-根据当前进度值绘制圆弧,
-注意圆弧的起始角度,如果是0度,标识的是3点钟方向**
绘制文字
-计算文字的baseline,计算方法在代码中也有,详细的推导过程,可以看下推荐的那篇文章
<declare-styleable name="CircleProgressView">
<attr name="progressTextSize" format="dimension|reference"/>
<attr name="progressTextColor" format="color|reference"/>
<attr name="progressBackGroundColor" format="color|reference"/>
<attr name="progressColor" format="color|reference"/>
<attr name="progressWidth" format="dimension|reference"/>
<attr name="progressStartAngle" format="integer|reference"/>
declare-styleable>
package com.hank.ok.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import com.hank.ok.progressstyle.R;
/**
* version:${version}
*/
public class CircleProgressView extends View {
private float mCenterX, mCenterY;//圆心坐标
private int mRadius;//圆半径
private int mStrokeWidth = 20;//进度条宽度
private Paint mPaintProgress, mPaintArc;
private int mProgressBackgroudColor=Color.LTGRAY;//进度条背景色
private int mProgressColor=Color.DKGRAY;//当前进度条颜色
private TextPaint mPaintText;
private int mTextColor=Color.RED;//文字颜色
private int mTextSize=36;//文字大小
private Paint.FontMetricsInt mFontMetrics;
private int mCurrentProgress = 0;//当前进度
private int mStartAngle = -90;//开始角度,注意0标识的是3点钟方向,所以这里默认从-90度开始
private int mSweepAngle = 0;//扫过的角度
public CircleProgressView(Context context) {
super(context);
init();
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//初始化自定义属性
TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView);
mTextColor=a.getColor(R.styleable.CircleProgressView_progressTextColor,Color.RED);
mTextSize=a.getDimensionPixelSize(R.styleable.CircleProgressView_progressTextSize,mTextSize);
mStrokeWidth=a.getDimensionPixelOffset(R.styleable.CircleProgressView_progressWidth,mStrokeWidth);
mProgressBackgroudColor=a.getColor(R.styleable.CircleProgressView_progressBackGroundColor,mProgressBackgroudColor);
mProgressColor=a.getColor(R.styleable.CircleProgressView_progressColor,mProgressColor);
mStartAngle=a.getInteger(R.styleable.CircleProgressView_progressStartAngle,mStartAngle);
//注意,一定得回收
a.recycle();
init();
}
private void init() {
//进度条背景画笔
mPaintProgress = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaintProgress.setStrokeWidth(mStrokeWidth);
mPaintProgress.setColor(mProgressBackgroudColor);
mPaintProgress.setStrokeJoin(Paint.Join.ROUND);
mPaintProgress.setStyle(Paint.Style.STROKE);
//当前进度画笔
mPaintArc = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaintArc.setStrokeWidth(mStrokeWidth);
mPaintArc.setStrokeJoin(Paint.Join.ROUND);
mPaintArc.setStyle(Paint.Style.STROKE);
mPaintArc.setColor(mProgressColor);
//文字画笔
mPaintText = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaintText.setColor(mTextColor);
mPaintText.setTextSize(mTextSize);
mFontMetrics = mPaintText.getFontMetricsInt();
}
/**入口方法,设置当前进度
* @param progress 当前进度0-100
*/
public void setProgress(int progress) {
this.mCurrentProgress = progress;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//如果没有模式不是EXACTLY,就设置默认大小为300
int resultSize = 0;
if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
resultSize = Math.min(widthSize, heightSize);
} else {
resultSize = 300;
}
setMeasuredDimension(resultSize, resultSize);
}
/**计算圆心坐标和半径
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2;
mRadius = getWidth() / 2 - mStrokeWidth / 2;//半径=view的宽度/2-进度条的宽度/1
}
@Override
protected void onDraw(Canvas canvas) {
drawProgressBg(canvas);
drawProgressArc(canvas);
drawProgressText(canvas);
}
/**绘制当前进度的圆弧
*
* 这里是要在一个矩形内切圆弧,
*
* 注意该矩形的宽度=View的宽度-圆弧的宽度
*
* 也就是中心点的坐标+-半径(半径也是要用View的宽度/2-圆弧的宽度/2的)
* @param canvas
*/
private void drawProgressArc(Canvas canvas) {
int left = (int) (mCenterX - mRadius);
int top = (int) (mCenterY - mRadius);
int right = (int) mCenterX + mRadius;
int bottom = (int) (mCenterY + mRadius);
RectF rectF = new RectF(left, top, right, bottom);
mSweepAngle = (int) (mCurrentProgress * 1.0F / 100 * 360);//扫过的角度
canvas.drawArc(rectF, mStartAngle, mSweepAngle, false, mPaintArc);
}
/**绘制文字
*
* 唯一要注意的就是计算文字的基线,
* baseline=中心点Y坐标+(mFontMetrics.bottom - mFontMetrics.top) / 2 - mFontMetrics.bottom);
* @param canvas
*/
private void drawProgressText(Canvas canvas) {
String progress = mCurrentProgress + "%";
float textWidth = mPaintText.measureText(progress);
int startX = (int) (mCenterX - textWidth / 2);
int baseline = (int) (mCenterY + (mFontMetrics.bottom - mFontMetrics.top) / 2 - mFontMetrics.bottom);
canvas.drawText(progress, startX, baseline, mPaintText);
}
/**绘制进度条背景色
* @param canvas
*/
private void drawProgressBg(Canvas canvas) {
// Log.i(">>>", "---------------->mCenterX:" + mCenterX + ",mCenterY:" + mCenterY + "---Radius:" + mRadius);
canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaintProgress);
}
}
<com.hank.ok.view.CircleProgressView
android:id="@+id/id_circle_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:progressTextColor="@color/colorAccent"
app:progressBackGroundColor="#aabbcc"
app:progressColor="@color/colorPrimary"
app:progressTextSize="16sp"
app:progressWidth="10dp"
app:progressStartAngle="-90"/>