drawText()中的文字基线

1. 关于drawText

可能以前平时对绘制文字要求不高,没怎么关注过文字基线的文字,只是知道有这么个概念.上一篇文章中有一个绘制简单进度条的例子,里边就有居中绘制文字的代码。
代码是这样的:

 float baseline = getHeight() * 2 / 3;

为什么我这样算呢?
因为文字的绘制,在Y轴来说是从基线开始的,基线就是四线格中的第三条线,它的位置刚好就是四线格的2/3的位置。所以这里就用 view的高度*2/3了。
这里因为是凑巧View的高度不是太高,所以并没有看出什么问题。想象一下如果View的高度是屏幕的高度,我还用View的高度的2/3位置作为基线还行么?

答案当然是不行了,基线的位置并不是单纯的从所在View的高度上决定的,而是根据文字的FontMetrics和View的中线确定的。

2. 推荐阅读

更加详细的解释请看drawText详解,这篇文章写得很清楚,而且也有推倒过程.

下边的例子就是使用大神文章中的方法计算的baseline做的一个环形进度条,加了自定义属性,可以在xml中设置文字的大小、文字颜色,进度条背景,进度条颜色,进度条宽度等。代码中有详细的注释。

效果图:
drawText()中的文字基线_第1张图片

图中的圆环进度条就是这次的代码效果

环形进度条思路:
1. 自定义属性
-在attrs文件中声明需要的属性

  1. 绘制背景圆环
    **-在onSizeChanged()方法中根据View的宽高计算中心点坐标和半径。
    -注意:这个圆环是View的内切圆,计算半径需要减去圆环的宽度**

  2. 绘制当前进度圆弧
    **-根据当前进度值绘制圆弧,
    -注意圆弧的起始角度,如果是0度,标识的是3点钟方向**

  3. 绘制文字
    -计算文字的baseline,计算方法在代码中也有,详细的推导过程,可以看下推荐的那篇文章

3.进度条代码

  1. 自定义属性
<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>
  1. 代码
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);
    }
}

4. 布局文件

<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"/>

你可能感兴趣的:(android)