テキストの描画(FontMetrics)

Last-modified: 2016-06-27 (月) 08:09:17

Canvasにテキストを描画する際など、思い通りの位置に描画するためにはFontMetricsを使用して座標を計算します。
Androidの場合も基本的な考え方はjava.awt.FontMetricsと同じです。

FontMetricsの基本

ポイントとなるのは下記の4つの座標でいずれもベースラインからの位置を示しています。
・FontMetrics.top
・FontMetrics.ascent
・FontMetrics.descent
・FontMetrics.bottom


図にすると下記のようなイメージになります。
fontmetrics.gif
がテキストを描画する際に指定した座標です。



// テキスト用ペイントの生成
Paint textPaint = new Paint( Paint.ANTI_ALIAS_FLAG);
textPaint.setTextSize( 35);
textPaint.setColor( Color.WHITE);
// FontMetricsの取得
FontMetrics fontMetrics = textPaint.getFontMetrics();
String text = "abcdefghijklmnopqrstu";
// 各座標の計算
float baseX = 0;
float baseY = 100;
float topY = baseY + fontMetrics.top;
float ascentY = baseY + fontMetrics.ascent;
float descentY = baseY + fontMetrics.descent;
float bottomY = baseY + fontMetrics.bottom;
// テキストの描画
canvas.drawText( text, baseX, baseY, textPaint);
// BaseLineの描画
Paint baseLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG);>
baseLinePaint.setColor( Color.RED);
canvas.drawLine(0, baseY, getWidth(), baseY, baseLinePaint);
// Baseポイントの描画
canvas.drawCircle( baseX, baseY, 5, baseLinePaint);
// TopLineの描画
Paint topLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG);
topLinePaint.setColor( Color.LTGRAY);
canvas.drawLine(0, topY, getWidth(), topY, topLinePaint);
// AscentLineの描画
Paint ascentLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG);
ascentLinePaint.setColor( Color.GREEN);
canvas.drawLine(0, ascentY, getWidth(), ascentY, ascentLinePaint);
// DescentLineの描画
Paint descentLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG);
descentLinePaint.setColor( Color.YELLOW);
canvas.drawLine(0, descentY, getWidth(), descentY, descentLinePaint);
// ButtomLineの描画
Paint bottomLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG);
bottomLinePaint.setColor( Color.MAGENTA);
canvas.drawLine(0, bottomY, getWidth(), bottomY, bottomLinePaint);

中心線上に文字列を描画

FontMetricsの基本でみたように、指定した位置に文字を描画しようとしても指定した座標はベースラインとして使用されるため、思っていたよりも上の位置に文字列が描画されてしまいます。

このような場合にAscent,Descentから座標を計算して調整する事で思った位置に文字列を描画する事ができます。


center.gif

// 中心座標
float centerX = getWidth() / 2;
float centerY = getHeight() / 2;
// 中心線の描画(この中心に来るように文字列を描画したい)
Paint centerLinePaint = new Paint( Paint.ANTI_ALIAS_FLAG);>
centerLinePaint.setColor( Color.YELLOW);
canvas.drawLine(0, centerY, getWidth(), centerY, centerLinePaint);
canvas.drawLine(centerX, 0, centerX, getHeight(), centerLinePaint);
// 描画する文字列
String text = "hijkl";
// テキスト用ペイントの生成
Paint textPaint = new Paint( Paint.ANTI_ALIAS_FLAG);
textPaint.setTextSize( 35);
textPaint.setColor( Color.WHITE);
FontMetrics fontMetrics = textPaint.getFontMetrics();
// 文字列の幅を取得
float textWidth = textPaint.measureText( text);
// 中心にしたいX座標から文字列の幅の半分を引く
float baseX = centerX - textWidth / 2;
// 中心にしたいY座標からAscentとDescentの半分を引く
float baseY = centerY - (fontMetrics.ascent + fontMetrics.descent) / 2;
// テキストの描画
canvas.drawText( text, baseX, baseY, textPaint);

吹き出しを描画

中心線上に文字列を描画で思った通りの位置にテキストを描画する事が出来たので、次にこれらのテキストに枠線等を書いて吹き出しを描画してみます。

 

完成形
balloon.gif

 
 

まず、文字列と文字列を囲む枠を描画します。
下のレイヤーから描画するので1.枠、2.文字列の順で描画します。
balloon1.gif

// 文字列用ペイントの生成
Paint textPaint = new Paint( Paint.ANTI_ALIAS_FLAG);
textPaint.setTextSize( 35);
textPaint.setColor( Color.WHITE);
FontMetrics fontMetrics = textPaint.getFontMetrics();
// 背景をテキストと同じ色で描画
canvas.drawRect( 0, 0, getWidth(), getHeight(), textPaint);
// テキストの中心の座標
float baseX = 100;
float baseY = 100;
String text = "hijkl";
// 文字列の幅を取得
float textWidth = textPaint.measureText( text);
// 文字列の幅からX座標を計算
float textX = baseX - textWidth / 2;
// 文字列の高さからY座標を計算
float textY = baseY - (fontMetrics.ascent + fontMetrics.descent) / 2;
// 吹き出し用ペイントの生成
Paint balloonPaint = new Paint( Paint.ANTI_ALIAS_FLAG);
balloonPaint.setTextSize( 35);
balloonPaint.setColor( Color.LTGRAY);
// 吹き出しの座標。文字列の5ポイント外側を囲む
float balloonStartX = textX - 5;
float balloonEndX = textX + textWidth + 5;
float balloonStartY = textY + fontMetrics.ascent - 5;
float balloonEndY = textY + fontMetrics.descent + 5;
// 吹き出しの描画
RectF balloonRectF = new RectF( balloonStartX, balloonStartY, balloonEndX, balloonEndY);
canvas.drawRoundRect(balloonRectF, 5, 5, balloonPaint);
// 文字列の描画
canvas.drawText( text, textX, textY, textPaint);
 

次に、枠の影とポイントを指す三角形の描画を追加します。

 
float balloonCenterX = ( balloonStartX + balloonEndX) / 2;
// 各座標を+2して吹き出しの影を描画
RectF balloonShadowRectF = new RectF( balloonStartX + 2, balloonStartY + 2, balloonEndX + 2, balloonEndY + 2);
canvas.drawRoundRect(balloonShadowRectF, 5, 5, balloonShadowPaint);
// ポイントを指す三角形を描画
int triangleWidth = (int)textX / 2;
int triangleHeight = triangleWidth / 2;
// 三角形の頂点を表わすポイントの生成
Point point = new Point( (int) balloonCenterX - triangleWidth / 2, (int)balloonEndY);
Point[] cornerPoint = new Point[3];
cornerPoint[ 0] = new Point( 0, 0);
cornerPoint[ 1] = new Point( triangleWidth, 0);
cornerPoint[ 2] = new Point( triangleWidth / 2, triangleHeight);
// 独自に定義したメソッド。 drawShape(canvas, point, cornerPoint, Color.GRAY);

  • 黒 -- 2016-06-27 (月) 08:09:17