Android で SVG
Android アプリで SVG を描画する方法について調べてみた。
ライブラリのプロジェクト組み込み
Android 標準では SVG 描画をサポートしていないため代わりに svg-android というライブラリを入手する。これは以下のサイトで公開されている。ライセンスは Apache License 2.0。
ライブラリは JAR ファイル形式。現時点の最新は svg-android-1.1.jar
。今回は SvgViewer という Android プロジェクトを作成して組み込んでみる。手順は以下のようになる。
- Eclipse 上で SvgViewer という Android プロジェクトを作成
- プロジェクトの直下に lib というフォルダを作成
- lib に JAR ファイルをコピー
- パッケージ・エクスプローラー上でプロジェクトを選択
- コンテキストメニューから「リフレッシュ」を選択、これで lib/svg-android-1.1.jar が表示されるはず
- パッケージ・エクスプローラー上の lib 内にある svg-android-1.1.jar を選択
- コンテキストメニューから「ビルド・パス」→「ビルド・パスに追加」を選択
組み込みに成功すると svg-android-1.1.jar がプロジェクト内の参照ライブラリとなり com.larvalabs.svgandroid
パッケージが使用できるようになる。
サンプル アプリ
いろいろな SVG ファイルを閲覧したいので簡単なファイラー機能を持ったアプリを作ってみる。仕様は以下。
- 開いているフォルダ内のファイル ( SVG のみ ) とサブ フォルダをリスト形式で表示
- 画面上部に開いているフォルダのパスを表示
- パスの隣にあるボタンを押すと上の階層に戻れる
- リスト上のファイルとサブ フォルダはアイコンを分ける
- アイコンはリソース内の SVG ファイルを利用する
- リスト上のサブ フォルダをタップすると中に移動
- リスト上のファイルをタップすると SVG ファイル画面に遷移
リスト表示する SVG 形式のアイコンは以下を使用する。
そのまま使うとリストには大きすぎるため Inkscape を利用して 48x48 にサイズ調整した。これらをプロジェクトの res/drawable
に ic_file.svg、ic_folder.svg というファイルとして格納して ListView
に関連づける Adapter
クラスで以下のように読み込む。
/**
* インスタンスを初期化します。
*
* @param context コンテキスト。
* @param textViewResourceId レイアウト。
* @param objects 管理対象とするアイテムのコレクション。
*/
public FileListItemAdapter( Context context, int textViewResourceId ) {
super( context, textViewResourceId );
this.mLayoutInflater = ( LayoutInflater )context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
Resources r = context.getResources();
this.mFileIcon = this.loadIcon( r, R.drawable.ic_file );
this.mFolderIcon = this.loadIcon( r, R.drawable.ic_folder );
}
/**
* SVG 形式のアイコンを Drawable として読み込みます。
*
* @param r リソース管理オブジェクトのインスタンス。
* @param id アイコンのリソース識別子。
*
* @return 読み込まれた Drawable。
*/
private Drawable loadIcon( Resources r, int id ) {
SVG svg = SVGParser.getSVGFromResource( r, id );
return svg.createPictureDrawable();
}
SVG の読み込みは com.larvalabs.svgandroid
パッケージに定義された SVGParser
クラスでおこなう。getSVGFrom~
系メソッドが複数あるので読み込み方法にあわせて選択する。これらのメソッドはすべて SVG
クラスのインスタンスを返す。
実際に描画するためのデータは SVG
インスタンスから取得する。コントロールの描画要素としたいなら SVG.createPictureDrawable
から得られる PictureDrawable
、Canvas
で画像の一部として扱いたい場合は SVG.getPicture
メソッドから Picture
といった感じで使い分ける。ファイル一覧の構築はフォルダが選択される度に以下のメソッドを呼び出して実行する。
/**
* ファイル一覧を更新します。
*
* @param dir ディレクトリ。
*/
private void updateFileList( File dir ) {
this.mCurrentDirTextView.setText( dir.getPath() );
// 親ディレクトリの有無で移動きりかえ
this.mParentDir = dir.getParent();
this.mUpDirButton.setEnabled( this.mParentDir != null );
// ファイル一覧の更新
{
this.mFileListAdapter.clear();
File[] files = dir.listFiles( mFilter );
if( files != null ) {
for( File file : files ) {
this.mFileListAdapter.add( file );
}
}
this.mFileListAdapter.notifyDataSetChanged();
}
}
mFileListAdapter
が前述の Adapter
クラス。ファイル ビューア系のサンプルでフォルダを開く度に Adapter
を作り直すものをよく見るが、今回のものは SVG から生成した画像をひとつのインスタンスで使い回したいため Adapter
自体を更新する方式にした。
ArrayAdapter.clear
ですべての要素を消去。以降は add
で追加されてゆく。要素の更新が完了したら ArrayAdapter.notifyDataSetChanged
を呼び出すことで ListView
側へ通知される。
もう一つ重要な機能としてファイルのフィルターがある。これは Java でお馴染みの FileFilter
を仕様。以下のように FileFilter
派生クラスを定義して File.listFiles(FileFilter)
に指定すればフィルターされる。
/**
* ファイル情報のコレクションから、SVG ファイルとディレクトリを抽出します。
*/
public class SvgFileFilter implements FileFilter {
/**
* SVG ファイルの拡張子。
*/
private static final String SVG_EXTENSION = ".svg";
/**
* フィルタリングを実行します。
*
* @param file ファイル情報。
*
* @return ファイル情報がフィルタリングの対象だった場合は true。それ以外は false。
*/
public boolean accept( File file ) {
return ( file.isDirectory() || file.getName().endsWith( SVG_EXTENSION ) );
}
}
リストから SVG ファイルが選ばれた時は、SVG を表示するための Activity
に遷移する。読み込み対象はストレージ上のファイルとなるので遷移元からファイルのパスを Intent.putExtra
で渡し、それから構築した InputStream
を指定して SVG を読む。
/**
* SVG ファイルを読み込みます。
*
* @param path SVG ファイルへのパス情報。
*/
private void loadSvg( String path ) {
try {
this.mStream = new FileInputStream( new File( path ) );
SVG svg = SVGParser.getSVGFromInputStream( this.mStream );
if( svg != null ) {
this.mSvgImageView.setImageDrawable( svg.createPictureDrawable() );
}
} catch( Exception e ) {
this.showErrorMessage( e.getMessage() );
}
}
アプリを実行すると以下のようになる。
最後にサンプル プログラムのプロジェクトを公開しておく。
Android 2.1 update 1 (API Level 7) でビルド、エミュレータと初代 Xperia (SO-01B) にて動作確認した。
感想
現時点では読めない SVG ファイルが多い。例えば OpenClipArt から入手したものはほとんどエラーになる。単純な図形でもレンダリングが崩れることもあるのでパーサーの作り込みが足りないのだろう。
また読み込まれるサイズが SVG の定義に依存する点も残念。Picture/PictureDrawable
になる前、つまりベクター状態でリサイズできるようになって欲しい。そうなれば GUI パーツとして利用しやすくなる。この辺りも将来に期待か。とはいえ SVG を Android の図形オブジェクトに変換するというアイディアは秀逸。将来は Android も WPF や Siliverlight のようにベクタ形式をサポートする可能性もあるが svg-android なら今すぐ対応できる。
今回のサンプルのように GUI リソースとして使えば画像を用いずに複雑な図形を描画できる。これは多くの解像度に対応しなければならない Android アプリにとって大きなメリットになりそうだ。
Comments from WordPress
- 通りすがり 2012-03-19T09:46:18Z
現時点では読めない SVG ファイルが多い。例えば OpenClipArt から入手したものは、 ほとんどエラーになる。単純な図形でもレンダリングが崩れることもあるので、 パーサーの作り込みが足りないのだろう。
svg-android は SVG のモバイル向けのプロファイルである SVG Basic 1.1 をサポートするライブラリなのでフルセットの SVG で作られた画像には対応していませんし今後も対応しないでしょう。