すこし前に iOS アプリ上で Font Awesome を利用する方法について調査してみたので、今回は Android アプリで試してみる。
Android アプリにおける組み込みフォント
Android の TextView などは Typeface を指定するとフォントを変更できる。Typeface のインスタンスは、フォントの参照方法ごとに用意された create 系メソッドから生成する。
組み込みフォントの場合、createFromAsset か createFromFile を利用する。前者が Android プロジェクトの assets、後者はファイルからフォントを参照する。以降にこれらの詳細をまとめる。
assets
Android プロジェクトには assets という仕組みがある。開発に Eclipse & ADT を使用しているならプロジェクト直下、Android Studio の場合は src/main に assets というフォルダを作成することで、その配下を特殊なリソースとして参照できるようになる。
というわけで、実際にプロジェクトへ際に読み込んでみよう。手順は以下のようになる。
- Android プロジェクト内の assets フォルダを開く
- assets フォルダ内へ Font Awesome に含まれる fontawesome-webfont.ttf をコピー
次に、assets からフォントを読み込むための処理を実装する。汎用なので、ユーティリティ的なクラスに static メソッドとして定義するとよいだろう。
/** * フォント関連のユーティリティです。 */ public class FontUtility { /** * フォントを assets から読み込みます。 * * @param context コンテキスト。 * @param path フォント ファイルを示す assets フォルダからの相対パス。 * * @return 成功時は Typeface インスタンス。それ以外は null。 */ public static Typeface getTypefaceFromAssets( Context context, String path ) { return Typeface.createFromAsset( context.getAssets(), path ); } }
Activity クラスのメソッド上で、さきほど組み込んだ assets/fonts から読み込む場合の処理は以下のようになる。
// フォント読み込み Typeface typeface = FontUtility.getTypefaceFromAssets( this, "fontawesome-webfont.ttf" ); // コントロールの表示フォント指定 TextView textView = ( TextView )this.findViewById( R.id.textView ); textView.setTypeface( typeface );
なお、assets におけるサイズには上限がある。諸説あるようだが、おおむね UNCOMPRESS_DATA_MAX という値が 1MB ~ 2MB ほどで定義されており、上限を超えると assets からの読み込みがエラーになる。
Font Awesome の場合、どのタイプのフォントも 1MB 未満なので、この制限はクリアしている。たとえばバージョン 3.2.1 における TTF ファイルは 78KB である。とはいえ assets の上限サイズを考えると、これでも結構、大きい。
また、assets には他のファイルを置くことも多い。特に階層を維持するという特性から、ローカル HTML に利用する事も考えられる。その場合、サイズだけでなくファイル構成の面でも管理しにくくなるかもしれない。
よって、なるべくなら別の場所へ組み込みたい。次項では、そのための方法を試す。
res/raw を利用したフォントの読み込み
Android のリソースには raw というカテゴリがある。これは標準でサポートされていないファイルをリソースとして扱いたい場合に利用する。まずは res/raw にフォントを組み込む。手順は以下のようになる。
- Android プロジェクト内の res 直下に raw というサブフォルダを作成
- raw 内へ Font Awesome に含まれる fontawesome-webfont.ttf をコピー
- fontawesome-webfont.ttf のファイル名を fontawesome.ttf に変更する
res/raw に配置されたファイルは id が割り当てられるのだが、名前にハイフンを利用できないので手順 3 が必要となる。リソースとして組み込まれたら Resources.openRawResource に id を指定することで、ファイルを InputStream として取得できる。
Typeface の create 系メソッドにはストリームを受け取るものはないけれど、ファイル化されたものなら読み込める。この仕組みを利用して res/raw から Typeface を得る方法が Stack Overflow で紹介されている。
Font in Android Library
この処理を前項で作成したクラスに追加してみる。
/** * フォントを res/raw から読み込みます。 * 実装は Stack Overflow に投稿された以下の記事を参考にしています。 * * Font in Android Library - Stack Overflow * http://stackoverflow.com/questions/7610355/font-in-android-library * * @param context コンテキスト。 * @param fileName リソース識別子。R.raw 以下に定義されたものを指定します。 * * @return 成功時は Typeface インスタンス。それ以外は null。 */ public static Typeface getTypefaceFromRaw( Context context, int resourceId ) { InputStream inputStream = null; BufferedOutputStream outputStream = null; Typeface typeface = null; try { // res/raw のフォントをメモリに読み込む inputStream = context.getResources().openRawResource( resourceId ); // フォントを一時ファイルとして出力 String fontFilePath = context.getCacheDir() + "/tmp" + System.currentTimeMillis() + ".raw"; outputStream = new BufferedOutputStream( new FileOutputStream( fontFilePath ) ); byte[] buffer = new byte[ inputStream.available() ]; int length = 0; while( ( length = inputStream.read( buffer ) ) > 0 ) { outputStream.write( buffer, 0, length ); } // フォントとして読み込んだら、一時ファイルを消す typeface = Typeface.createFromFile( fontFilePath ); new File( fontFilePath ).delete(); } catch( NotFoundException e ) { Log.e( "UseFontAwesome", "Could not find font in resources!", e ); } catch( IOException e ) { Log.e( "UseFontAwesome", "Error reading in font!" ); } finally { tryClose( inputStream ); tryClose( outputStream ); } return typeface; } /** * 破棄すべきリソースを持つオブジェクトを開放します。 * * @param obj オブジェクト。 */ private static void tryClose( Closeable obj ) { if( obj != null ) { try { obj.close(); } catch( IOException e ) { e.printStackTrace(); } } }
res/raw から読み込んだリソースを一時ファイルとして出力し、Typeface.createFromFile で参照している。Activity クラスのメソッド上で、さきほど res/raw に組み込んだ fontawesome.ttf を読む処理は、以下のようになる。
// フォント読み込み Typeface typeface = FontUtility.getTypefaceFromRaw( this, R.raw.fontawesome ); // コントロールの表示フォント指定 TextView textView = ( TextView )this.findViewById( R.id.textView ); textView.setTypeface( typeface );
assets、res/raw のどちらにも共通することだが、アプリで組み込みフォントを利用する場合、なんども読み込むのは非効率なので Typeface はキャッシュしたほうがよさそう。例えば Application 派生クラスで一度だけ読み込み、以降は Context.getApplication 経由で参照するようにする、など。
アイコン一覧の表示
Font Awesome のフォントを読み込めるようになったので、表示を試す。以前、iOS で作成したサンプルのように、Font Awesome の持つアイコンと対応する UNICODE を一覧にしてみよう。
アイコンを TextView に表示する場合、フォントと文字の指定が必要となる。前者は前項などで解説済み。後者については、UNICODE の値から String を生成することになる。処理は以下のようになる。
// アイコンとして表示する文字の生成 char unicode = 0xF016; String iconText = String.valueOf( unicode ); // アイコン表示 TextView textView = ( TextView )this.findViewById( R.id.iconTextView ); textView.setText( iconText ); // UNICODE 表示 textView = ( TextView )this.findViewById( R.id.unicodeTextView ); textView.setText( String.format( "%04X", ( int )unicode ) );
Font Awesome の持つ UNICODE の範囲は font-awesome.css を参照のこと。欠番もあるが、現時点では F000 ~ F18B となる。実際に ListView で表示すると以下のようになる。
ばっちり。なお、サンプルではフォントの読み込みについて、assets と res/raw の両方を試せるようにしている。
さまざまなコントロール上の表示
Android のコントロールでテキスト部分を持つものは、たいてい TextView から派生している。たとえば Button や CheckBox、RadioButton など。これらに Font Awesome のアイコンを適用する場合は TextView に対する処理を転用できて便利だ。
もし私が設計するなら、せいぜい View あたりからの派生に留めるだろう。とはいえ、テキスト表示機能の汎用性を考慮するなら、このような継承もありなのかもしれない。レイアウト系や ImageView などの例外を除き、代表的なコントロールの大半がそう設計されているのも面白い。
というわけで、TextView 派生の代表的なコントロールを対象に、アイコンを指定してみた。対象は以下。
実際に表示してみると、以下のようになる。
ばっちり。
なお、TextView 派生のコントロールに Font Awesome のアイコンを指定する場合はテキスト扱いとなるため、レイアウト XML 上からサイズ指定できる。今回のサンプルでは android:textSize=”20sp” を設定してみた。
サンプル プロジェクト
今回、作成したサンプル プログラムのプロジェクト一式は GitHub で公開している。
開発は Eclipse 4.3 + ADT、動作確認には Xperia NX ( Android 4.0.4 ) と Android 4.3 シミュレーターを使用。ライセンスは The MIT License (MIT)。fork、改変などはご自由にどうぞ。