2012年4月23日月曜日

Androidでテキストを動的に画像化するには?

Androidでテキストを動的に画像化するにはどうしたらいいだろう。

例えば、こんな風。


Eclipseプロジェクトファイル:CreateBitmapText.zip(152KB)


res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#660000" />

</LinearLayout>


CreateBitmapTextActivity.java
package jp.example;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;


public class CreateBitmapTextActivity extends Activity {


  @Override
  public void onCreate(Bundle savedInstanceState) {

    // お約束
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Paint作成
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setColor(Color.WHITE);
    paint.setTextSize(22);

    // 描画テキストの領域を取得
    // rect.widthとmtwは違う。rect.widthの方が小さい。
    // rect.heightとfmHeightは違う。rect.heightの方が小さい。
    String txt = "Bitmap Text : abcdefghijklmnopqrstuvwxyx";
    Rect rect = new Rect();
    paint.getTextBounds(txt, 0, txt.length(), rect);
    FontMetrics fm = paint.getFontMetrics();
    int mtw = (int) paint.measureText(txt);
    int fmHeight = (int) (Math.abs(fm.top) + fm.bottom);
    Log.v("onCreate", "rect.w=" + rect.width() + " rect.h=" + rect.height() + " fm.top=" + fm.top + " fm.bottom=" + fm.bottom + " fm.ascent=" + fm.ascent + "fm.descent=" + fm.descent + " fm.height=" + fmHeight + " mtw=" + mtw);

    // 描画領域ピッタリのビットマップ作成
    int margin = 1; // ギリギリすぎるので上下左右に多少余裕を取る
    Bitmap bmp = Bitmap.createBitmap(mtw + margin * 2, fmHeight + margin * 2, Bitmap.Config.ARGB_8888);

    // ビットマップからキャンバスを作成してテキスト描画
    Canvas cv = new Canvas(bmp);
    cv.drawText(txt, margin, Math.abs(fm.ascent) + margin, paint);

    // ビットマップテキストをImageViewにセット
    ImageView iv = (ImageView) findViewById(R.id.iv);
    iv.setImageBitmap(bmp);

  }

}

第1のポイントは描画領域(ビットマップの大きさ)をいかに計算するか。

描画するテキストの幅・高さをPaintから取得する方法はいくつかあるが、適当だと思われたのは

  • 幅:paint.measureText()のもの
  • 高さ:FontMetricsのtopの絶対値とbottomを足したもの

だった。

上のスクリーンショットで赤い部分は描画領域(ビットマップの大きさ)を表現している。ImageViewのbackgroundの設定で描画していて、ビットマップ自体は透明。


第2のポイントはdrawText()の描画位置はテキストの左下ベースラインだというところ。

これを理解していないとBitmap上の思わぬところに描画して訳がわからなくなるので注意が必要だった。