speg03の雑記帳

主に未来の自分のために試したことなどを記録しています

TextViewのテキストサイズ指定

TextViewなどのテキストサイズをJavaのコードから動的に変更しようとして、はまりそうだったのでメモ。

ちなみに今回の実行環境はAndroid 2.2 (API Level 8)を使った。この環境だとデフォルトで以下のようなAndroidManifest.xmlの設定が有効になっていることになる。

<supports-screens android:anyDensity="true" />

Android 1.6 (API Level 4)よりも低い場合はanyDensityのデフォルト値がfalseになる。anyDensityの値がfalseの場合はまた話が変わってくるので注意が必要。

このあたりの話は以下を参考に。

テキストサイズの定義と取得方法

このページのDimensionの項を見るとテキストサイズの定義とXMLファイルでの取得方法、Javaコードでの取得方法がわかる。以下のような感じ。

テキストサイズの定義
<resources>
  <dimen name="font_size">16sp</dimen>
<resources>
XMLファイルでのテキストサイズの取得方法
@dimen/font_size
Javaコードでのテキストサイズの取得方法
Resources res = getResources();
float fontSize = res.getDimension(R.id.font_size);

試してみる

XMLファイルでの指定とJavaコードでの指定のそれぞれについて2種類のテキストサイズを指定してみる。

res/values/dimens.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <dimen name="font_size_small">16sp</dimen>
  <dimen name="font_size_large">32sp</dimen>
</resources>
res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <!-- 小さいテキスト(XML指定) -->
  <TextView
      android:id="@+id/smallTextXml"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textSize="@dimen/font_size_small"
      android:text="Small Text (XML)" />

  <!-- 小さいテキスト(コード指定) -->
  <TextView
      android:id="@+id/smallTextCode"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Small Text (Code)" />

  <!-- 大きいテキスト(XML指定) -->
  <TextView
      android:id="@+id/largeTextXml"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textSize="@dimen/font_size_large"
      android:text="Large Text (XML)" />

  <!-- 大きいテキスト(コード指定) -->
  <TextView
      android:id="@+id/largeTextCode"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Large Text (Code)" />

</LinearLayout>

XML指定と書いてあるTextViewはtextSize属性に先ほど定義したテキストサイズを指定している。コード指定と書いてあるTextViewはJavaコードからテキストサイズを指定するためここでは指定していない。

TextSizeSampleActivity.java (間違ったコード)
import android.app.Activity;
import android.content.res.Resources;
import android.os.Bundle;
import android.widget.TextView;

public class TextSizeSampleActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Resources res = getResources();
        float fontSizeSmall = res.getDimension(R.dimen.font_size_small);
        float fontSizeLarge = res.getDimension(R.dimen.font_size_large);

        // 小さいテキストのテキストサイズを指定
        TextView smallTextCode = (TextView) findViewById(R.id.smallTextCode);
        smallTextCode.setTextSize(fontSizeSmall);

        // 大きいテキストのテキストサイズを指定
        TextView largeTextCode = (TextView) findViewById(R.id.largeTextCode);
        largeTextCode.setTextSize(fontSizeLarge);
    }

}

先ほどの例にしたがってテキストサイズを取得。setTextSizeメソッドでテキストサイズを指定している。

実行結果(間違ったコード)

HVGAの場合


WVGA800の場合

HVGAの場合はXML指定とコード指定で同じ大きさになっているので問題がなさそうだけど、WVGA800の場合はXML指定とコード指定で大きさが異なっている。

修正

正しいコードは以下のようになる。

TextSizeSampleActivity.java (正しいコード)
import android.app.Activity;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.TypedValue;
import android.widget.TextView;

public class TextSizeSampleActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Resources res = getResources();
        float fontSizeSmall = res.getDimension(R.dimen.font_size_small);
        float fontSizeLarge = res.getDimension(R.dimen.font_size_large);

        // 小さいテキストのテキストサイズを設定
        TextView smallTextCode = (TextView) findViewById(R.id.smallTextCode);
        // ※ 設定値の単位を指定するようにした
        smallTextCode.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizeSmall);

        // 大きいテキストのテキストサイズを設定
        TextView largeTextCode = (TextView) findViewById(R.id.largeTextCode);
        // ※ 設定値の単位を指定するようにした
        largeTextCode.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizeLarge);
    }

}

実行結果(正しいコード)

WVGA800の場合

これでXML指定とコード指定で同じ大きさになった。

何が問題だったか

要するに

Resources res = getResources();
float fontSize = res.getDimension(R.id.font_size);

このコードで取得できるテキストサイズはpx単位なのに

TextView#setTextSize(float)

これで指定するテキストサイズがsp(scaled pixel)単位だったのが問題だった。

sp単位は160dpiを1として実際の解像度に合わせた比率で実際のpx単位を求める。なので、sp単位の値を受け取るsetTextSizeメソッドが計算済みのpx単位の値を受け取ったので大きさがずれてしまっていた。

HVGAの場合で問題がなかったのはHVGAの仮想デバイスを作成するときデフォルトで解像度が160dpiになっているためだった。