Quantcast
Channel: アカベコマイリ » Android
Viewing all articles
Browse latest Browse all 10

Android で SVG

$
0
0

Android アプリで SVG を描画する方法について調べてみた。

ライブラリのプロジェクト組み込み

Android 標準では SVG 描画をサポートしていないので、代わりに svg-android というライブラリを入手する。これは以下のサイトで公開されている。ライセンスは Apache License 2.0 となる。

ライブラリは JAR ファイルとして配布されている。現時点の最新は svg-android-1.1.jar。

今回は SvgViewer という Android プロジェクトを作成し、そこへ組み込んでみる。手順は以下のようになる。

  1. Eclipse 上で SvgViewer という Android プロジェクトを作成
  2. プロジェクトの直下に lib というフォルダを作成
  3. lib に JAR ファイルをコピー
  4. パッケージ・エクスプローラー上でプロジェクトを選択
  5. コンテキストメニューから「リフレッシュ」を選択、これで lib/svg-android-1.1.jar が表示されるはず
  6. パッケージ・エクスプローラー上の lib 内にある svg-android-1.1.jar を選択
  7. コンテキストメニューから「ビルド・パス」→「ビルド・パスに追加」を選択

組み込みに成功すると svg-android-1.1.jar がプロジェクト内の参照ライブラリとなり、com.larvalabs.svgandroid パッケージが使用できるようになる。

サンプル アプリ

いろいろな SVG ファイルを閲覧したいので、簡単なファイラー機能を持ったアプリを作ってみる。仕様は以下のようになる。

  • 開いているフォルダ内のファイル ( SVG のみ ) とサブ フォルダをリスト形式で表示
  • 画面上部に開いているフォルダのパスを表示
  • パスの隣にあるボタンを押すと上の階層に戻れる
  • リスト上のファイルとサブ フォルダはアイコンを分ける
  • アイコンはリソース内の SVG ファイルを利用する
  • リスト上のサブ フォルダをタップすると中に移動
  • リスト上のファイルをタップすると SVG ファイル画面に遷移

リストに表示する SVG 形式のアイコンは、以下を使用する。

そのまま使うとリストには大きすぎるため、Inkscape を利用して 48×48 にサイズ調整した。

これらをプロジェクトの 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() );
    }
}

アプリを実行すると、以下のようになる。

サンプル アプリ

サンプル アプリ

最後に、サンプル プログラムのプロジェクトを公開しておく。

サンプル プロジェクト
SvgViewer.zip 36.8KB

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 アプリにとって、大きなメリットになりそうだ。


Viewing all articles
Browse latest Browse all 10

Trending Articles