Android アプリで Font Awesome を利用する
すこし前に 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 で紹介されていた。
この処理を前項で作成したクラスに追加してみる。
/**
* フォントを 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
に対する処理を転用できて便利だ。
しかし設計として疑問がある。私だったら TextView
ではなく View
派生あたりに留めるだろう。そのうえで TextView
が必要なら包含にする。とはいえテキスト表示を汎用で重要なものと位置づけるるなら Android のような設計もありなのかもしれない。レイアウト系や 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、改変などはご自由にどうぞ。
Comments from WordPress
- t.ebinuma 2014-12-07T07:54:53Z
Androidのカスタムフォントで検索してたどり着きました。
ありがとうございます