今天在自定义view中加上一个文字显示。
本以为,这个挺简单的,但是没想到文字加上后,发现不居中。
设置了 mPaintTv.setTextAlign(Paint.Align.CENTER);
发现只能文字左右居中,上下依然不是正中心。like this
中间的红线是中心线,但是文字却不是跟中心对齐的,而是文字底部跟中心线平齐。
这显然不符合按钮的显示啊,查阅了一下资料,发现,自定义view在drawText时候,这个文字的对齐还真的是有个小坑。
上图是从网上找到的,但是里面有一个坑,后面会讲。
**先解释下,drawText(String text,float x,float y,Paint paint)
里面的x参数,在paint未设置setTextAlign
时,默认是从横坐标x点处开始绘制第一个字符串的字符。
如果mPaintTv.setTextAlign(Paint.Align.CENTER);
则是整个字符串的中心点的横坐标是x
里面的y参数永远都跟 字符串的Baseline对齐。**
这也就是为什么我们用矩形的中心点去绘制text的时候,文字却不在正中心,反而是有些向上偏移。
like this:
所以绘制的时候我们要把文字偏移的那段距离算出来:
那么坑就出来了 :【前方高能】
通过Paint.FontMetrics fm = mPaintTv.getFontMetrics();
可以获得字体属性类FontMetrics
而 FontMetrics 里面有两个很重要的属性就是ascent 和 descent
他们表示的不仅仅是如下图的距离,如果你debug过,会发现他们还带有正负号,
说实话,当时我感觉真是日了哈士奇了。后来我发现,这也是可以理解的。
就如图所示,以baseline为x轴,向下是正方向,向上为负方向。
所以ascent永远都是负值。
搞清楚了这一点,我们就可以大大方方的计算了。
如果想要文字居中显示,肯定是以中心线(虚线)为x轴了,但是现在是以baseline为x轴,只需要计算出中心线和baseline之间的距离即可(中心线与baseline之间的距离 = 中心线到底部的距离 - descent)。
首先:
中心线到底部的距离是 = 整个文字的高度 / 2 ;即(-ascent + descent)/2
中心线与baseline之间的距离 = (-ascent + descent)/ 2 - descent ;即 (-ascent-descent) / 2
然后,只需要在drawText时候将y参数向上提高 中心线与baseline之间的距离即可
canvas.drawText("bjjg",maxWidth/2,maxHeight/2 - (fm.descent - (-fm.ascent + fm.descent)/2),mPaintTv);
全部代码如下:
/**
* Created by FanHaiChao on 2017/4/12.
* 自定义的带动画的view
*/
public class MyButtonView extends View {
private Paint mPaintBg;
private Paint mPaintTv;
private RectF rect;
private float radius;
private int index = 0;
private int maxIndex = 24;
private boolean isDraw = false;//是否绘制完成:true--绘制完成;false--未绘制完成
private boolean isTo0 = false;//true:变显示, false:变消失
public MyButtonView(Context context) {
this(context,null);
}
public MyButtonView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MyButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void initView() {
mPaintBg = new Paint();
mPaintBg.setAntiAlias(true);
mPaintBg.setStyle(Paint.Style.FILL);
mPaintBg.setColor(Color.RED);
mPaintTv = new Paint();
mPaintTv.setAntiAlias(true);
mPaintTv.setStyle(Paint.Style.FILL);
mPaintTv.setColor(Color.WHITE);
mPaintTv.setTextSize(36);
mPaintTv.setTextAlign(Paint.Align.CENTER);
}
//每一帧 onDraw一次 index变一次 ondraw一次
//注意onDraw 方法 会在view所在activity可见时调用,也就是说 view 重新可见时 也会调用onDraw
@Override
protected void onDraw(Canvas canvas) {
if (isDraw) {
float maxWidth = getWidth();
float maxHeight = getHeight();
if (index <= (maxIndex / 2)) {//前一半时间画圆,后一半时间画圆角矩形
canvas.drawCircle(maxWidth / 2, maxHeight / 2, radius, mPaintBg);
radius = ((maxHeight / 2) / (maxIndex / 2)) * index;
} else {
if (maxWidth > maxHeight) {
radius = maxHeight / 2;
} else {
radius = maxWidth / 2;
}
float wr = ((maxWidth / 2 - radius) / (maxIndex / 2)) * (index - maxIndex/2);
//参数:左边距,上边距,右边距,下边距
rect = new RectF(maxWidth / 2 - radius - wr, 0, maxWidth / 2 + radius + wr, 2 * radius);
canvas.drawRoundRect(rect, 90, 90, mPaintBg);
canvas.drawLine(0,maxHeight/2,maxWidth,maxHeight/2,mPaintTv);
Paint.FontMetrics fm = mPaintTv.getFontMetrics();
canvas.drawText("暴躁的码字猴",maxWidth/2,maxHeight/2 - (fm.descent - (-fm.ascent + fm.descent)/2),mPaintTv);
}
if (isTo0) {
index--;
} else {
index++;
index = index <= maxIndex ? index : maxIndex;
}
if (index <= maxIndex && index >= -1) {
invalidate();
} else {
System.out.println(index);
}
}
}
public void startShow(boolean draw) {
isDraw = draw;
if (index == maxIndex){
isTo0 = true;
radius= 0;
}else {
isTo0 = false;
radius= 0;
}
invalidate();
}
}