AIRのファイルシステムコンポーネント概要

前回の記事では、AIRのファイルシステムAPIの基礎となるflash.filesystem.Fileクラスについて詳細に解説した。Fileクラスを用いれば、ファイルやディレクトリの操作を非常に簡単に行うことができるうえ、非同期処理もサポートされている。

だが、AIRの本当の魅力はデスクトップアプリケーション開発に特化した、豊富なコンポーネントが組み込みで用意されていることにある。ファイルシステムAPIに関してもその例外ではなく、以下に示すように、豊富なヴィジュアルコンポーネントが利用可能だ。

  • ファイル選択、ファイル保存、ファイル複数選択などのダイアログ。OSネイティブのものを使用できるため、機能も豊富で使い勝手も良い。
  • ファイルシステムのツリー表示、データグリッドを利用した一覧表示などを行えるFlexコンポーネント。バインディングも利用できるので、非常に短いコードで高度な処理が可能になる。

ではこれらを解説していこう。

OSネイティブのファイル選択/保存ダイアログ

先ほども述べたとおり、プラットフォームが提供しているファイル選択、ディレクトリ選択、ファイル保存、ファイル複数選択などのダイアログをそのまま利用することができる。この恩恵は大きい。機能が非常に豊富なのは勿論のこと、利用者が普段から慣れ親しんでいるUIをそのまま利用できるため、一切の違和感なくファイル操作が行えるのが良い。

ダイアログを表示するのも簡単だ。flash.filesystem.FileクラスのbrowseFor~というメソッドを呼び出すだけだ。例えば、Cドライブを起点にファイル選択ダイアログを表示するには以下のようなコードを書けばよい。

var cdrive:File = new File("C:");
// 引数は、ダイアログのタイトル
cdrive.browseForOpen("ダイアログのタイトル");

またbrowseFor~メソッドは、ファイルのフィルタを指定することもできる。指定すると、ファイル選択ダイアログに表示されるファイルの種類を絞り込んだり、ファイル保存の際の拡張子を限定したりすることができる。ファイルのフィルタはflash.net.FileFilterクラスで表される。C++のソースファイルだけをフィルタリングするためには、以下のようなコードになる。

// ファイルフィルタ。
// コンストラクタの第一引数はフィルタの表示名、
// 第二引数は受け付けるファイル名を「;」でつないで記述(ワイルドカード可)
var cppFilter:FileFilter =
  new FileFilter("C++ソースファイル", "*.c;*.cpp;*.cc;*.c++");

var cdrive:File = new File("C:");
// 第二引数にファイルフィルタの配列を指定する
cdrive.browseForOpen("ダイアログのタイトル", [cppFilter]);

実行結果は以下のようになる。

ファイル選択ダイアログ

また、ダイアログで選択されたファイルを取得するにはFileクラスにイベントリスナを登録しておく必要がある。flash.events.EventクラスのSELECTイベントを指定し、イベントリスナを登録する。

var cdrive:File = new File("C:");

// ファイル選択イベントのリスナを登録
cdrive.addEventListener(Event.SELECT, function(event:Event):void {
    // イベントのターゲットが選択されたファイルなので、`File`型に変換
    var selectedFile:File = File(event.target);
    trace(selectedFile.name);
});

今まで挙げた例は単一ファイルを選択するためのダイアログだったが、同様のメソッドは他にもある。

  • browseForDirectory - ディレクトリ選択用のダイアログを開く。
  • browseForSave - ファイル保存用ダイアログを開く。
  • browseForOpenMultiple - ShiftキーやCtrlキーを用いることで複数選択可能なダイアログ。イベントの種別がflash.event.FileListEvent.SELECT_MULTIPLE、イベントリスナの引数がflash.event.FileListEventとなる。

また、これらのダイアログは複数同時に開くことができない。開こうとした場合エラーが発生するので、Errorのインスタンスを補足するためのtry/catchでこれらのコードを囲む必要がある。

ファイルを取り扱うFlexコンポーネント

次に、ファイルの取り扱いが可能なFlexコンポーネントを紹介したい。以下のようなものが挙げられる。

  • mx:FileSystemComboBox - 単一ディレクトリを選択できるコンボボックス。現在選択中のディレクトリまでのディレクトリ階層が、コンボボックスのアイテムとして表示される。
  • mx:FileSystemDataGrid - 単一ディレクトリ内のファイル/ディレクトリの情報をグリッドで一覧表示するコンポーネント。グリッド内のディレクトリをダブルクリックするとそのディレクトリ内を一覧表示するよう、グリッドの内容が切り替わる。ファイルを選択するとイベントが発生する。
  • mx:FileSystemList - 単一ディレクトリ内のファイル/ディレクトリをリスト表示する。その他の特徴はFileSystemDataGridと同じ。
  • mx:FileSystemTree - 単一ディレクトリ内のファイル/ディレクトリをツリー表示する。そのディレクトリよりも上位のディレクトリは見ることができない。その他の特徴はFileSystemDataGridと同じ。

これらのコンポーネントを全て使用したのが以下のサンプルアプリケーションだ。

ファイルシステムコンポーネントのサンプルアプリケーション

これらのコンポーネントはすべて連動しており、どれか一つのコンポーネント内でディレクトリを選択すると、他のコンポーネント全てがそのディレクトリを起点とするように切り替わる。

「C:\Documents and Settings」をデータグリッド内でダブルクリックすると…

他のコンポーネントも全て連動して切り替わる

また、コンポーネント内でファイルを選択すると、そのファイル名をアラートで表示する。

ファイル選択時はアラートで表示する

右上のボタンを押すと、ネイティブのファイル選択ダイアログが表示される。

ファイル選択ダイアログも表示

以上が、サンプルアプリケーションの機能だ。ソースコードは以下のようになっている。コード中のポイントになる部分は、後で詳しく説明する。

AIRFileSystemExample.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" height="498" applicationComplete="init();">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.FileEvent;
            import flash.filesystem.File;

            // (1) 選択されたディレクトリを格納する変数           
            [Bindable]
            private var dir:File;

            // (2) applicationCompleteイベント(アプリケーションの初期化完了後)に呼び出される
            private function init():void {
                // ユーザのホームディレクトリをディレクトリの初期値に指定
                dir = File.userDirectory;
            }

            // (3) OSネイティブのファイル選択ダイアログを表示する
            private function showFileChooser():void {
                if (!dir) return;
                dir.addEventListener(Event.SELECT, function(event:Event):void {
                    onFileSelected(event.target as File);
                });
                try {
                    dir.browseForOpen(dir.name);
                } catch (e:Error) {
                    trace(e.getStackTrace());
                }
            }
            // ファイル選択時の処理
            private function onFileSelected(selectedFile:File):void {
                if (!selectedFile || selectedFile.isDirectory)
                    return;
                Alert.show("選択されたファイルは" + selectedFile.nativePath);
            }
        ]]>
    </mx:Script>

    <!-- (4) ファイルシステムコンポーネントの利用 -->
    <mx:FileSystemComboBox id="combo" x="10" y="58" directory="{dir}"/>
    <mx:FileSystemDataGrid id="grid" x="10" y="114" directory="{dir}" select="onFileSelected(event.file)"/>
    <mx:FileSystemList id="list" x="10" y="303" directory="{dir}" select="onFileSelected(event.file)"/>
    <mx:FileSystemTree id="tree" x="212" y="303" directory="{dir}" select="onFileSelected(event.file)"/>

    <!-- (5) コンポーネント→変数へのバインディングを指定 -->
    <mx:Binding source="combo.directory" destination="dir"/>
    <mx:Binding source="grid.directory" destination="dir"/>
    <mx:Binding source="list.directory" destination="dir"/>
    <mx:Binding source="tree.directory" destination="dir"/>

    <mx:Label x="10" y="39" text="FileSystemCombobox" width="143" fontWeight="bold"/>
    <mx:Label x="10" y="95" text="FileSystemDataGrid" fontWeight="bold"/>
    <mx:Label x="10" y="286" text="FileSystemList" fontWeight="bold"/>
    <mx:Label x="212" y="286" text="FileSystemTree" fontWeight="bold"/>
    <mx:Button x="212" y="58" label="このボタンを押すと、ファイル選択ダイアログ表示" click="showFileChooser()"/>
</mx:WindowedApplication>

(1) 変数dirに設定されたディレクトリが、全てのファイルシステムコンポーネントの起点ディレクトリとなる。以下に示すように「Binding」メタデータタグを使用することで、コンポーネントとの連動を自動化している。

[Bindable]
private var dir:File;

(2) ルート要素「WindowedApplication」のイベント属性「applicationComplete」で、このメソッドの呼び出しを行っているため、アプリケーションの初期化直後にinit()メソッドが呼び出される。(1)で示した変数dirに、初期値としてユーザのホームディレクトリをセットしている。

private function init():void {
    // ユーザのホームディレクトリをディレクトリの初期値に指定
    dir = File.userDirectory;
}

(3) これは、ネイティブのファイル選択ダイアログを呼び出すボタンが押された時の処理だ。(1)で示した変数dirに対し、メソッドbrowseForOpenを呼び出しているのみだ。今回の記事前半で説明した通りである。

try {
    dir.browseForOpen(dir.name);
} catch (e:Error) {
    trace(e.getStackTrace());
}

(4) ファイルシステムコンポーネントを使用しているのはこの部分だ。

<mx:FileSystemComboBox id="combo" x="10" y="58" directory="{dir}"/>
<mx:FileSystemDataGrid id="grid" x="10" y="114" directory="{dir}" select="onFileSelected(event.file)"/>
<mx:FileSystemList id="list" x="10" y="303" directory="{dir}" select="onFileSelected(event.file)"/>
<mx:FileSystemTree id="tree" x="212" y="303" directory="{dir}" select="onFileSelected(event.file)"/>

全てのコンポーネントにdirectory属性があり、これがコンポーネントの起点となるディレクトリを表す。属性値として{dir}という式を用いており、(1)で示した変数dirとバインディングしていることを理解しよう。これにより、変数dirの値が変化したとき自動的にコンポーネントの内容が切り替わる。

また、selectイベント属性にActionScriptの式を書くことで、ファイル選択時の処理を記述している。ちなみに、ディレクトリ選択時の処理を記述するには、directoryChangeイベントをハンドリングする。

(5) (1)と(4)で示したように、「変数→コンポーネント」という方向のバインディングは確立されている。だが、コンポーネント内でディレクトリを選択されたら変数dirにそのディレクトリを格納する、つまり「コンポーネント→変数」という方向のバインディングが実現できていない。以下のような定義を併用することで、コンポーネントと変数の間の双方向バインディングを実現している。

<!-- コンボボックスの「directory」属性を、変数「dir」にバインディング -->
<mx:Binding source="combo.directory" destination="dir"/>

これにより、コンポーネントのどれかでディレクトリを選択したら

  1. 対象コンポーネントのdirectory属性が変化する
  2. 選択されたディレクトリが変数dirに格納される
  3. バインディングにより、他のコンポーネントのdirectory属性も変更され、表示が切り替わる

と言うように、全てのコンポーネントが連動するようになる。ぜひ、サンプルを実際に動作させて動きを確認してみていただきたい。

まとめ

今回は、OSネイティブのファイル選択ダイアログと、AIRが持つファイルシステムコンポーネントを紹介した。どちらも非常に簡単に取り扱うことができることがお解りだろうか。

今回は紹介しなかったが、Fileクラスのuploadメソッドとdownloadメソッドを用いると、ファイルのアップロード/ダウンロードを簡単に行うこともできる。APIの取り扱いは、今回ご紹介した内容とほとんど変わらないのでぜひお試しいただきたい。