このマスターシリーズでは、Flex 4.5 SDKの新機能の中から「Spark DataGrid」「Spark Form」「Spark Formatter」について解説します。

本記事では、Flex 4.5 SDKから追加されたSparkコンポーネントである「Spark Form」「Spark Formatter」「Spark Image」について紹介します。なお、Flash Builder 4.5.1にバンドルされている標準のFlex SDK(バージョン 4.5.1.21328)を対象にしています。

Spark Form

Flex 4.5 SDKでは、データグリッド系コンポーネントとして新たにSpark DataGridコンポーネントが追加されました。データグリッドは、特に業務アプリケーションにおいて非常に重要なコンポーネントの1つです。これまでMX DataGridコンポーネントがありましたが、よりよいデータグリッド系コンポーネントはFlex 4の頃からずっと待望されていたコンポーネントと言えるでしょう。

Spark DataGridの注目すべき点は何といってもパフォーマンス面における大きな向上です。これまでMX DataGridを利用する際に問題となっていた動作の”重さ”が大幅に軽減され、非常に利用しやすくなりました。特に列が多数ある場合の水平スクロールなどでは顕著なパフォーマンスの違いを感じることができます。サンプルSparkDataGridExampleは、この両者を比較できるようにしたものです。

SparkのFormコンポーネントは、以下の機能を提供します。

  • フォームのレイアウトの制御。
  • フォームのフィールドが入力必須かオプションかの明示。
  • フォームのデータのチェックと検証。
  • エラーメッセージの処理。

また、スタイルシートを使用してフォームの外観を設定することもできます。

※現在、FlexモバイルプロジェクトでSpark Formを使用することは推奨されていません。主にPC版Flex アプリケーションで使用します。

Spark Formのコンポーネント種別

以下の表は、フォームを作成するときに使用するコンポーネント一覧です。

フォームを作成するときに使用するコンポーネント一覧
コンポーネント タグ 概要
Form <s:Form> フォーム全体に対するコンテナやフォーム全体のレイアウトを定義します。フォームの内容は、FormHeadingやFormItem コンポーネントを使用して定義します(他のタイプのコンポーネントを使用することも可能です)。
FormHeading <s:FormHeading> 外観専用のコンポーネントです。フォーム内の見出しを定義します。
FormItem <s:FormItem> フォームに収容する子コンポーネントを、水平方向や垂直方向に揃えて配置します。子コンポーネントには、TextInput やCheckBoxなどのコントロールや、その他のコンテナなどを使用することが可能です。また、Validatorクラスと連動して、エラーメッセージの表示をサポートします。1つのFormに複数のFormItemを配置することが可能です。
FormLayout <s:FormLayout> Formのスキンで使用されているデフォルトのレイアウトクラスです。通常、直接編集することはありません。

Spark Formのスキン種別

Formコンポーネントには、デフォルトのスキン以外に、代替スキンクラスが用意されています。どちらのスキンもFormItemを垂直にレイアウトしますが、異なる点はFormItemと内包された子コンポーネントのレイアウトです。

以下の表は、コンポーネントとスキンの対応表です。

フォームのコンポーネントとスキンの対応表
コンポーネント デフォルトスキン 代替スキン
Form spark.skins.spark.FormSkin spark.skins.spark.StackedFormSkin
FormHeading spark.skins.spark.FormHeadingSkin spark.skins.spark.StackedFormHeadingSkin
FormItem spark.skins.spark.FormItemSkin spark.skins.spark.StackedFormItemSkin

Spark Formのサンプル

次のコードは、ユーザー名(メールアドレス)入力/パスワード入力/パスワード確認入力といった3つの入力項目を保持したフォームのサンプルです(サンプル:SparkFormSample.fxp)。それぞれの入力項目には、バリデーターが用意されており、必要に応じてエラーメッセージを表示します。アプリケーションの上部にあるトグルボタンで、デフォルトスキンと代替スキンを切り替えることができます。

ソースコード:SparkFormSample/src/SparkFormSample.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Spark Form Sample --> <s:Application xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" xmlns:mx = "library://ns.adobe.com/flex/mx" fontSize = "15" > <fx:Declarations> <fx:Array id="validators"> <mx:EmailValidator source="{usernameTextInput}" property="text" /> <mx:StringValidator source="{passwordTextInput}" property="text" minLength="4" maxLength="10" /> <mx:StringValidator source="{confirmPasswordTextInput}" property="text" minLength="4" maxLength="10" /> </fx:Array> </fx:Declarations> <fx:Script> <![CDATA[ import mx.controls.Alert; import mx.core.UIComponent; import mx.events.ValidationResultEvent; import mx.validators.Validator; import spark.skins.spark.FormHeadingSkin; import spark.skins.spark.FormItemSkin; import spark.skins.spark.FormSkin; import spark.skins.spark.StackedFormHeadingSkin; import spark.skins.spark.StackedFormItemSkin; import spark.skins.spark.StackedFormSkin; protected const STYLE_PROPERTY :String = "skinClass"; protected var formCSS :CSSStyleDeclaration; protected var formHeadingCSS :CSSStyleDeclaration; protected var formItemCSS :CSSStyleDeclaration; protected override function createChildren():void { super.createChildren(); formCSS = styleManager .getStyleDeclaration("spark.components.Form"); formHeadingCSS = styleManager .getStyleDeclaration("spark.components.FormHeading"); formItemCSS = styleManager .getStyleDeclaration("spark.components.FormItem"); Alert.buttonWidth = 120; Alert.buttonHeight = 50; } protected function comboBoxChangeHandler(event:Event):void { if (toggleButton.selected) { formCSS.setStyle(STYLE_PROPERTY, StackedFormSkin); formHeadingCSS.setStyle(STYLE_PROPERTY, StackedFormHeadingSkin); formItemCSS.setStyle(STYLE_PROPERTY, StackedFormItemSkin); } else { formCSS.setStyle(STYLE_PROPERTY, FormSkin); formHeadingCSS.setStyle(STYLE_PROPERTY, FormHeadingSkin); formItemCSS.setStyle(STYLE_PROPERTY, FormItemSkin); } } protected function validateAll():void { var i :int; var l :int; var results :Array; var alert :Alert; var event :ValidationResultEvent; var errorMessages :String = ""; results = Validator.validateAll(validators) l = results.length; if(0 < l) { for(i = 0; i < l; i++) { event = results[i] as ValidationResultEvent; errorMessages += String("[" + event.target.source.id + "]\n" + event.message + "\n\n"); } alert = Alert.show(errorMessages, "Error"); alert.width = 640; alert.height = 480; // { element:****, position:***} // // element:UIComponent // 無効な下位要素エレメント // // position:Vector.<int> // 表示リストツリーのエレメントの位置を示す整数の Vector です。 // このプロパティは配列のソートに使用されます。 var o :Object; var elements :Array; elements = form.invalidElements; l = elements.length; for(i = 0; i < l; i++) { o = elements[i]; logTextArea.appendText( ( o.element as UIComponent ).id + "\t::\t" + ( o.position as Vector.<int> ).toString() + "\n" ); } } } protected function resetAll():void { usernameTextInput.text = ""; passwordTextInput.text = ""; confirmPasswordTextInput.text = ""; usernameTextInput.errorString = ""; passwordTextInput.errorString = ""; confirmPasswordTextInput.errorString = ""; } ]]> </fx:Script> <s:layout> <s:VerticalLayout paddingLeft = "20" paddingTop = "20" paddingRight = "20" paddingBottom = "20" /> </s:layout> <s:controlBarContent> <s:ToggleButton id="toggleButton" label="Stacked forms" change="comboBoxChangeHandler(event);" /> </s:controlBarContent> <s:Scroller width="100%" height="100%" verticalScrollPolicy="on"> <s:Group width="100%" height="100%"> <s:Form id="form" width="100%" height="100%" defaultButton="{submitButton}" backgroundColor="#eeeeee"> <s:FormHeading width="100%" fontSize="35" label="Spark Form Heading" backgroundColor="#000000" color="#ffffff" /> <s:FormItem sequenceLabel="1." label="Username:" required="true"> <s:helpContent> <s:Label text="user@domain.com" /> </s:helpContent> <s:TextInput id="usernameTextInput" /> </s:FormItem> <s:FormItem sequenceLabel="2." label="Password:" required="true"> <s:helpContent> <s:Label text="hoge" color="#ff0000" /> </s:helpContent> <s:TextInput id="passwordTextInput" displayAsPassword="true" /> </s:FormItem> <s:FormItem sequenceLabel="3." label="Confirm password:" required="true"> <s:helpContent> <s:HGroup> <s:Label text="fuga" color="#ff0000" /> <s:Label text="piyo" color="#0000ff" /> </s:HGroup> </s:helpContent> <s:TextInput id="confirmPasswordTextInput" displayAsPassword="true" /> </s:FormItem> <s:FormItem> <s:layout> <s:HorizontalLayout /> </s:layout> <s:Button id="submitButton" height="50" label="Submit" click="validateAll()" /> <s:Button id="resetButton" height="50" label="Reset" click="resetAll()" /> </s:FormItem> </s:Form> </s:Group> </s:Scroller> <s:TextArea id="logTextArea" width="100%" height="200" /> </s:Application>

Spark Formatter

FormatterクラスはFlex 4以前から用意されていましたが、Flex 4.5からはSparkに3つのグローバリゼーション関連のFormatterクラスが追加されました。

Spark Formatterの種別

以下の表は、SparkのFormatterクラスの一覧です。

SparkのFormatterクラス一覧
コンポーネント 概要
CurrencyFormatter ロケールに依存した通貨値のフォーマットと解析を可能にします。
DateTimeFormatter ロケールに依存したDate オブジェクトのフォーマットを提供します。
NumberFormatter ロケールに依存した、数値のフォーマットと解析を提供します。

これらのクラスは、全てspark.globalizationパッケージのクラスです。同名のクラスがflash.globalizationパッケージにも存在しますが、異なる点は、spark.globalizationパッケージはMXMLで使用できることと、要求されたロケール名("ja_JP", "en_US", "fr_FR" など)にロケールスタイルを使用し、バインド可能なメソッドやプロパティを持っていることです。

Spark Formatterのサンプル

次のコードは、ComboBoxで任意のロケール名を選択したタイミングで、各Formatterを実行するサンプルです(サンプル:SparkFormatterSample.fxp)。Formatterのフォーマットは、各クラスともにformat()メソッドで実行されますが、このメソッドはバイディングに対応しているため、MXML上の「{}」(波括弧)内に直接記述することが可能です。

ソースコード:SparkFormatterSample/src/SparkFormatterSample.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Spark Formatter Sample --> <s:Application xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" xmlns:mx = "library://ns.adobe.com/flex/mx" locale = "{localeComboBox.selectedItem}" fontSize = "35" > <fx:Declarations> <s:CurrencyFormatter id="cf" /> <s:DateTimeFormatter id="df" /> <s:NumberFormatter id="nf" /> </fx:Declarations> <s:layout> <s:VerticalLayout verticalAlign="middle" horizontalAlign="center" /> </s:layout> <s:Form> <s:FormItem label="Locale"> <s:ComboBox id="localeComboBox" width="800" requireSelection="true"> <s:dataProvider> <s:ArrayList> <fx:String>ar_AE</fx:String> <fx:String>de_DE</fx:String> <fx:String>en_US</fx:String> <fx:String>ja_JP</fx:String> <fx:String>fr_FR</fx:String> <fx:String>it_IT</fx:String> <fx:String>ko_KR</fx:String> <fx:String>he_IL</fx:String> <fx:String>nl_NL</fx:String> <fx:String>ru_RU</fx:String> <fx:String>sk_SK</fx:String> <fx:String>zh_CN</fx:String> <fx:String>zh_TW</fx:String> </s:ArrayList> </s:dataProvider> </s:ComboBox> </s:FormItem> <s:Rect width="100%" height="2"> <s:fill> <s:SolidColor color="#333333" /> </s:fill> </s:Rect> <s:FormItem label="Currency"> <s:TextInput width="800" text="{cf.format(currencyStepper.value)}" /> <s:NumericStepper id = "currencyStepper" width = "800" value = "{Math.random() * 100000000}" maximum = "100000000" stepSize = "0.1" /> </s:FormItem> <s:FormItem label="Date"> <s:TextInput width="800" text="{df.format(dateField.selectedDate)}" /> <mx:DateField id="dateField" width="800" selectedDate="{new Date()}" /> </s:FormItem> <s:FormItem label="Number"> <s:TextInput width="800" text="{nf.format(numberStepper.value)}" /> <s:NumericStepper id = "numberStepper" width = "800" value = "{Math.random() * 100000000}" maximum = "100000000" stepSize = "0.01" /> </s:FormItem> </s:Form> </s:Application>

Spark Image

SparkのImageコンポーネントは、スキナブルコンポーネントサブクラスで、コンポーネントの外観を飾るクロム、ローディング状態を表すプリローダー、エラー状態を表すアイコンなど、カスタマイズ可能なスキンパーツで構成されており、JPEG/PNG/GIFファイルを読み込み表示することができます。

なお、画像の表示自体は、内部にあるBitmapImageグラフィックエレメントが機能しているので、BitmapImageでできることは一通り実現可能です。

Spark Imageのサンプル

次のコードは、Imageコンポーネントにネットワーク上の画像を表示させるサンプルです(サンプル:SparkImageSample.fxp)。回線が遅く、かつImage.enableLoadingStateスタイルプロパティがtrueの場合は、画像をダウンロードしている間にプリローダーが表示されます(図8)。また、「Dummy URL」 ボタンを押したときには、読み込みエラーを表すアイコンを表示します(図9)。「clip」「repeat」「scale」と書かれたボタンバーでは、Imageに表示された画像の配置方法を設定することができます(図10~12)。

ソースコード:SparkImageSample/src/SparkImageSample.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Spark Image Sample --> <s:Application xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" fontSize = "25" fontWeight = "bold" > <fx:Script> <![CDATA[ import spark.events.IndexChangeEvent; protected function buttonTaigaClickHandler():void { var url_:String = "https://www.adobe.com/jp/devnet/images/160x160/taiga_hirohata_160x160.jpg"; image.source = new URLRequest(url_); } protected function buttonEiichiClickHandler():void { var url_:String = "https://www.adobe.com/jp/devnet/images/160x160/arikawaeiichi_160x160.jpg"; image.source = new URLRequest(url_); } protected function buttoToshimitsuClickHandler():void { var url_:String = "https://www.adobe.com/jp/devnet/images/160x160/adc_toshi_160x160.jpg"; image.source = new URLRequest(url_); } protected function buttonDummyClickHandler():void { var url_:String = "invalidURL"; image.source = new URLRequest(url_); } protected function buttonBarChangeHandler(event:IndexChangeEvent):void { image.fillMode = String(buttonBar.selectedItem); } ]]> </fx:Script> <s:VGroup width="100%" height="100%" gap="10" paddingTop="10" paddingBottom="10" horizontalAlign="center" verticalAlign="middle"> <s:HGroup> <s:Button width="200" height="50" label="taiga" click="buttonTaigaClickHandler()" /> <s:Button width="200" height="50" label="eiichi" click="buttonEiichiClickHandler()" /> <s:Button width="200" height="50" label="toshimitsu" click="buttoToshimitsuClickHandler()" /> <s:Button width="200" height="50" label="Dummy URL" click="buttonDummyClickHandler()" /> </s:HGroup> <s:ButtonBar id="buttonBar" requireSelection="true" change="buttonBarChangeHandler(event)"> <s:dataProvider> <s:ArrayList> <fx:String>clip</fx:String> <fx:String>repeat</fx:String> <fx:String>scale</fx:String> </s:ArrayList> </s:dataProvider> </s:ButtonBar> <s:Image id = "image" width = "100%" height = "100%" backgroundAlpha = "1" backgroundColor = "#990000" enableLoadingState = "true" fillMode = "clip" smooth = "true" smoothingQuality = "{checkBox.selected ? 'high' : 'default'}" /> <s:CheckBox id = "checkBox" label = "{ checkBox.selected ? 'smoothingQuality : high' : 'smoothingQuality : default'}" /> </s:VGroup> </s:Application>

おわりに

以上がFlex 4.5 SDK から追加された「Spark Form」「Spark Formatter」「Spark Image」コンポーネントの紹介でした。FormとImageは、Sparkのスキンアーキテクチャに対応して以前のバージョンのものより拡張が容易になりましたので、ぜひ試していただきたいです。

そして、Formatterクラスは、主にグローバリゼーション対応のFormatterが一新したということで、単一のアプリケーションで多言語対応コンテンツを開発するとき、特にPC版よりも世界に配布するようなモバイル版のアプリケーションを開発するときに役に立つと思われます。

これらのチュートリアルを確認し、Flexモバイルアプリケーションのスキン機構を知ることによって、実際にアプリケーション開発に携わるとき、より現実的な設計を行うことができるでしょう。

関連記事

Flex 4.5 SDK マスターシリーズ 第1回 コーディング環境の大幅な強化