自定义View文本居中问题

文本结构

其实是没有文本结构这个概念的,所谓的文本结构,是作者定义出来的用来描述文本绘制到显示屏的一个概念,其中包含着BaseLine、Ascent、Descent、Leading。


自定义View文本居中问题_第1张图片
文本.png

BaseLine也就是我们常说的基线,Ascent、Descent、以及Leading这些都是字体设计的规范。

现实现一个计步器,效果图如下:


自定义View文本居中问题_第2张图片
计步器.png

看起来实现很简单,在onDraw方法上,先勾画出一个圆,然后在此区域上再画出一段弧出来,最后将文本绘制上去。
代码如下

 paint.setStyle(Paint.Style.STROKE);
        paint.setColor(CIRCLECOLOR);
        paint.setStrokeWidth(RING_WIDTH);
        canvas.drawCircle(getWidth()/2,getHeight()/2,RADIUS,paint);


        paint.setColor(Color.GREEN);
        paint.setStrokeCap(Paint.Cap.ROUND);
        canvas.drawArc(getWidth()/2-RADIUS,getHeight()/2-RADIUS,
                getWidth()/2+RADIUS,getHeight()/2+RADIUS,-90,225,
                false,paint);

        paint.setTextSize(140);
        paint.setStyle(Paint.Style.FILL);
        paint.setTextAlign(Paint.Align.CENTER);
        paint.getTextBounds(demoString,0,demoString.length(),bounds);
        canvas.drawText(demoString,getWidth()/2,getHeight()/2,paint);

运行后的效果:


自定义View文本居中问题_第3张图片
效果图.png

可以发现,即使我们设置了Paint.Align.CENTER的属性,虽然在水平方向上,文本是剧中的,但是在竖直方向上,文本是有所偏移的。
那么问题就来了,这是Bug吗?
不是,因为我们是按照Android的APi写出来的,这既不是我们的Bug也不是Google的Bug,当然产品验收的时候,会十分肯定地跟你说,你这是个Bug。

为什么说这不是Bug呢?

仔细一看,你就会发现,文本底部穿过圆心。


自定义View文本居中问题_第4张图片
效果图2.png

BaseLine的位置刚好是居中的,文本绘制是以BaseLine的纵坐标开始绘制的。所以我们只需要将BaseLine的位置减去一个偏移量,就可以获得偏移之后的BaseLine的位置,文本也就能居中了。
偏移就是文本长度的一半,如何获得?
这里有两种方法。

1、通过文本绘制区域获取

文本在绘制过程中,本就是一块矩形区域。所以可以通过获得文本绘制区域的top与bottom的差,来得到偏移量。

Rect bounds=new Rect();//1

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        ...
       paint.getTextBounds(demoString,0,demoString.length(),bounds);
       float offest=(bounds.top + bounds.bottom)/2;
       canvas.drawText(demoString,getWidth()/2,getHeight()/2-offest,paint);
}


注释1通过实例化一个Rect对象,再通过Paint的getTextBounds方法计算获得Rect的值。
但是利用Rect获得的偏移去实现居中,在列表滚动的时候居中文字会出现跳动,因为不同的文本的Rect是不一样的,虽然最后都能实现居中,但是在产品看来,这是属于不稳定的,甚至是一个Bug。

2、利用Ascent与Descent

由于Ascent与Descent是字体级别的设置,动态的文本并不会导致这个两个值变动。通过Ascent与Descent拿到的偏移,是稳定的,不变的。同时,它的偏移计算只计算一次就可以了,并不用放到onDraw方法里,我们通过FontMetrics来获得这些数值。

Paint.FontMetrics fontMetrics=new Paint.FontMetrics();//1
String demoString="12030步";
    {
        paint.setTextSize(200);
        paint.setTextAlign(Paint.Align.CENTER);
        paint.getFontMetrics(fontMetrics);//2
    }

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        ...
        float offest=(fontMetrics.ascent+ fontMetrics.descent)/2;
        canvas.drawText(demoString,getWidth()/2,getHeight()/2-offest,paint);
    }
}

在注释1可以看到FontMetrics是Paint的一个静态类,在注释2里面通过getFontMetrics计算得到FontMetrics的值。

你可能感兴趣的:(自定义View文本居中问题)