monaka android memo

Androidの勉強中!調べたことを忘れないためのブログです!

TextViewのフォントサイズを自動調整する。

限られたTextViewの中に、もし大きいフォントサイズを指定してしまった時に、
Viewに収まるようにフォントサイズを自動調整できないかなーと考えたので、実際にやってみたことをメモします。

まず横幅ですが、下記のURLを参考にしました。
https://gist.github.com/STAR-ZERO/2934490

次に、縦幅については調べてみても見つからなかったので、縦幅と横幅を自動リサイズするクラスを作成しました。

import android.content.Context;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;

/**
 * フォントサイズ自動調整TextView
 */
public class ResizeTextView extends TextView
{
	/**
	 * コンストラクタ
	 * @param context
	 */
	public ResizeTextView(Context context)
	{
		super(context);
	}

	/**
	 * コンストラクタ
	 * @param context
	 * @param attrs
	 */
	public ResizeTextView(Context context, AttributeSet attrs)
	{
		super(context, attrs);
	}

	/**
	 * 子Viewの位置を決める
	 */
	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom)
	{
		super.onLayout(changed, left, top, right, bottom);
		resize();
	}

	/**
	 * テキストサイズ調整
	 */
	private void resize()
	{
		/** 最小のテキストサイズ */
		final float MIN_TEXT_SIZE = 10f;

		int viewHeight = this.getHeight();	// Viewの縦幅
		int viewWidth = this.getWidth();	// Viewの横幅

		// テキストサイズ
		float textSize = getTextSize();

		// Paintにテキストサイズ設定
		Paint paint = new Paint();
		paint.setTextSize(textSize);

		// テキストの縦幅取得
		FontMetrics fm = paint.getFontMetrics();
		float textHeight = (float) (Math.abs(fm.top)) + (Math.abs(fm.descent));

		// テキストの横幅取得
		float textWidth = paint.measureText(this.getText().toString());

		// 縦幅と、横幅が収まるまでループ
		while (viewHeight < textHeight | viewWidth < textWidth)
		{
			// 調整しているテキストサイズが、定義している最小サイズ以下か。
			if (MIN_TEXT_SIZE >= textSize)
			{
				// 最小サイズ以下になる場合は最小サイズ
				textSize = MIN_TEXT_SIZE;
				break;
			}

			// テキストサイズをデクリメント
			textSize--;

			// Paintにテキストサイズ設定
			paint.setTextSize(textSize);

			// テキストの縦幅を再取得
			fm = paint.getFontMetrics();
			textHeight = (float) (Math.abs(fm.top)) + (Math.abs(fm.descent));

			// テキストの横幅を再取得
			textWidth = paint.measureText(this.getText().toString());
		}

		// テキストサイズ設定
		setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
	}
}


簡単に説明すると、TextViewを継承したクラスで、onLayout()でフォントサイズを調整するresize()を呼んでます。
下記にresize()について説明していきます。


1.まずTextViewの縦幅と横幅を取得します。

int viewHeight = this.getHeight();	// Viewの縦幅
int viewWidth = this.getWidth();	// Viewの横幅


2.TextViewに設定されているフォントサイズを取得し、Paintクラスにフォントサイズをセットします。

// テキストサイズ
float textSize = getTextSize();

// Paintにテキストサイズ設定
Paint paint = new Paint();
paint.setTextSize(textSize);


3.テキストの縦幅、横幅を取得します。
FontMetricsと、Paintを使用することで、実際にテキストが表示される時のサイズを取得することができます。

// テキストの縦幅取得
FontMetrics fm = paint.getFontMetrics();
float textHeight = (float) (Math.abs(fm.top)) + (Math.abs(fm.descent));

// テキストの横幅取得
float textWidth = paint.measureText(this.getText().toString());	


4.TextViewに収まるまでテキストサイズをデクリメントしていきます。

// 縦幅と、横幅が収まるまでループ
while (viewHeight < textHeight | viewWidth < textWidth)
{
	// 調整しているテキストサイズが、定義している最小サイズ以下か。
	if (MIN_TEXT_SIZE >= textSize)
	{
		// 最小サイズ以下になる場合は最小サイズ
		textSize = MIN_TEXT_SIZE;
		break;
	}

	// テキストサイズをデクリメント
	textSize--;

	// Paintにテキストサイズ設定
	paint.setTextSize(textSize);

	// テキストの縦幅を再取得
	fm = paint.getFontMetrics();
	textHeight = (float) (Math.abs(fm.top)) + (Math.abs(fm.descent));

	// テキストの横幅を再取得
	textWidth = paint.measureText(this.getText().toString());
}


5.最後に調整したテキストサイズを、TextViewにセットします。

// テキストサイズ設定
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);

これを下記のXMLで実行してみます。
上のTextViewはいつものやつで、下のが今回のクラスです。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:text="AaYy"
        android:textSize="200dp"
        android:background="#ffff66"
        android:bufferType="spannable" />

    <com.example.resizetextview.ResizeTextView
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:text="AaYy"
        android:textSize="200dp"
        android:background="#ccff99" 
        android:bufferType="spannable"/>

</LinearLayout>

f:id:monakapro:20130802130021p:plain

いつものTextViewに対し、大きいフォントサイズを指定すると当然はみ出てしまいますが、
今回のクラスではフォントサイズを調整して表示されています。


XMLのTextViewに追加している下記ですが、

android:bufferType="spannable"

これをいれないと、テキストのベースラインが変更されず、
Viewの横幅だけ小さくなっていき、縦幅は大きいフォントサイズの時のままになることがあったからです(´・ω・`)



ここから余談です!
View継承したクラスで、Canvas使ってテキスト書いてみたけど、かなり重かったのでやめました。
あと、フォントの縦幅って結構面倒くさくて、実際の文字サイズ+余白があります。
その余白はなにに使用するかというと、今回のXMLで小文字の「y」とか、下とか上にはみ出る文字がある時のためみたいです。
もし数字限定のTextViewで、余白も詰めたい!って人は、FontMetricsを詳しく調べると良いかもしれません(`・ω・´)

はてなブログでコード表示するときに、もうちょっと見やすくできないかなぁ...