このチュートリアルでは、Flex4で新たに追加されたSparkコンポーネントとSparkスキンアーキテクチャについて詳しく紹介します。
このチュートリアルにおいて、「Haloコンポーネント」という用語は、今までのFlexに含まれていたFlexフレームワークコンポーネントを指します。「Sparkコンポーネント」という用語は、Flex 4における新しいコンポーネントを指します。
Sparkスキンアーキテクチャは、Sparkコンポーネントのロジックと見た目を明確に分離する特徴があります。Sparkスキンアーキテクチャを理解するには、大きく3つポイントがありますので、順に説明していきます。
SparkコンポーネントをSparkスキンに対応させる場合、必ずSkinnableComponentを継承します。
次に、Sparkスキンに対応させるためには、下記の要素を定義します。
次に、SkinnableComponentを継承したSparkコンポーネントは、現在のスキンステートをgetCurrentSkinState()から取得します。そのため自身のプロパティの値やスキンパートのプロパティなどを参照して、スキンステート名を返すようにオーバーライドします。
また、マウス操作イベントやキー入力イベントのハンドリングの処理やプロパティの変更の時にスキンの状態を更新するためには、invalidateSkinState()を呼び出します。
例)ButtonBaseコンポーネントの場合
ButtonBaseの場合は、下記のようにいくつかのプロパティを参照して、現在のスキンステートを返しています。
override protected function getCurrentSkinState():String
{
if (!enabled)
return "disabled";
if (isDown())
return "down";
if (hovered || mouseCaptured)
return "over";
return "up";
}
スキンステートとスキンパート定義をするためには、メタデータを使います。
スキンステート:[SkinState(スキンステート名)]
スキンパート:[SkinPart]
例) ButtonのベースとなるButtonBaseクラスのスキン定義
ButtonBaseには、up,over,down,disabledの4種類のスキンステートが定義されています。スキンパートとして、ボタンのラベルを表示するためにlabelDisplayというテキスト表示用コンポーネントが定義されています。
また、ButtonBaseの定義は、ButtonBaseを継承しているButtonでも有効です。
[SkinState("up")]
[SkinState("over")]
[SkinState("down")]
[SkinState("disabled")]
public class ButtonBase extends SkinnableComponent …
{
[SkinPart]
public var labelDisplay:TextBase;
}
Sparkスキンは、spark.skins.SparkSkinを使って作成します。そして、Sparkコンポーネントに定義されたスキンステート名でステートを定義します。スキンパート名でコンポーネントを定義します。
また、ステート毎のプロパティの値は、Flex 4から新しく採用されたステート構文を使って容易に行えます。また、スキンパートには、Sparkコンポーネントを用いることが出来ます。
次に、SparkスキンがどのSparkコンポーネントのスキンなのかを定義するためにメタデータを使います。[HostComponent(スキン対象となるコンポーネント名)]
FlashBuilder 4は、HostComponentを指定するとスキンのステートとSparkコンポーネントのスキンステートに相違がないかチェックします。
例) ButtonコンポーネントとButtonSkin
Buttonコンポーネントは、継承元のButtonBaseでスキン定義されています。また、Buttonコンポーネントの標準Sparkスキンは、spark.skins.spark.ButtonSkinです。ButtonSkinは、HostComponentでButtonのスキンということを示しています。
スキンステートは、コンポーネントに定義されている名称でスキン側にもステートが定義されています。スキンパートであるlabelDisplayは、Labelで定義されています。
このようにSparkコンポーネントのスキン定義を参照しながら、Sparkスキンを作成します。
[SkinState("up")]
[SkinState("over")]
[SkinState("down")]
[SkinState("disabled")]
public class ButtonBase extends SkinnableComponent …
{
[SkinPart]
public var labelDisplay:TextBase;
}
spark.skins.spark.ButtonSkin
<s:SparkSkin ... >
<fx:Metadata>
<![CDATA[
[HostComponent("spark.components.Button")]
]]>
</fx:Metadata>
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<s:Label id="labelDisplay" .../>
</s:SparkSkin>
ここでは、カスタムSparkコンポーネントとカスタムSparkスキンを作成してみましょう。作成するコンポーネントは、一般的なログオンフォームのコンポーネントです。まずは、下記のようにSparkコンポーネント設計を行います。
クラス
| 役割 | 説明 |
|---|---|
| コンポーネント | froms.LogonForm |
| スキン | skins.LogonFormSkin |
| イベント | events.LogonEvent |
スキンパート
| パート名 | 説明 |
|---|---|
| logonButton | ログオンを実行するButtonコンポーネント |
| logonUserName | ログオンユーザの名前を入力するためのTextInputコンポーネント |
| logonUserPassword | ログオンユーザのパスワードを入力するためのTextInputコンポーネント |
プロパティ
| プロパティ名 | 説明 |
|---|---|
| isLogoned | ログオンされているかどうか。 この値がtrueの場合は、スキンステートをlogonedに変更する。falseの場合は、スキンステートをnormalに変更する。 |
イベント
| イベント名 | 説明 |
|---|---|
| logon | ログオンしていない時にlogonButtonをクリックしたときのイベント |
| logoff | ログオンしている時にlogonButtonをクリックしたときのイベント |
スキンステート
| ステート名 | 説明 | ||||||
|---|---|---|---|---|---|---|---|
| normal | 通常時のステート
|
||||||
| logoned | ログオン済みのステート
|
||||||
| disabled | コントロールが無効な時のステート |
設計が終わったら、ログオンフォームコンポーネントを作成します。
isLogonedの値が設定されたことで、現状のスキンステートから変更あったことにするためにinvalidateSkinState ()を呼び出しています。
package forms
{
import events.LogonEvent;
import flash.events.MouseEvent;
import spark.components.Button;
import spark.components.SkinnableContainer;
import spark.components.TextInput;
import spark.components.supportClasses.SkinnableTextBase;
import spark.components.supportClasses.TextBase;
[Event(name="logon", type="events.LogonEvent")]
[Event(name="logoff", type="events.LogonEvent")]
[SkinState("logoned")]
public class LogonForm extends SkinnableContainer
{
[SkinPart]
public var logonButton:Button;
[SkinPart]
public var logonUserName:TextInput;
[SkinPart]
public var logonUserPassword:TextInput;
protected var _isLogoned:Boolean;
public function get isLogoned():Boolean{
return _isLogoned;
}
public function set isLogoned(value:Boolean):void{
_isLogoned = value;
invalidateSkinState();
}
public function LogonForm()
{
super();
}
private function logonButtonClickHandler(event:MouseEvent):void{
if( _isLogoned ){
dispatchEvent(new LogonEvent(LogonEvent.LOGOFF));
} else {
dispatchEvent(new LogonEvent(LogonEvent.LOGON));
}
}
protected override function initializationComplete():void{
super.initializationComplete();
logonButton.addEventListener(MouseEvent.CLICK,logonButtonClickHandler,false,0,true);
}
protected override function getCurrentSkinState():String{
if( enabled ){
if( _isLogoned ){
return "logoned"; //ログオン済み
} else {
return "normal"; //ログオンしていない
}
} else {
return "disabled"
}
}
}
}
次に、ログオンフォームコンポーネントのカスタムSparkスキンを作成します。
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
alpha.disabled="0.5"
>
<fx:Metadata>
<![CDATA[
[HostComponent("forms.LogonForm")]
]]>
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
<s:State name="logoned" />
</s:states>
<s:Rect id="background" left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor id="bgFill" color="#FFFFFF"/>
</s:fill>
</s:Rect>
<s:VGroup id="contentGroup" left="0" right="0" top="0" bottom="0" minWidth="0" minHeight="0">
<s:HGroup width="100%" height="100%">
<s:Label text="ユーザ名" width="150" textAlign="right"/>
<s:TextInput id="logonUserName" skinClass="skins.FormTextInput"
editable="true"
editable.logoned="false"
borderVisible="true"
borderVisible.logoned="false"
/>
</s:HGroup>
<s:HGroup width="100%" height="100%">
<s:Label text="パスワード" width="150" textAlign="right"/>
<s:TextInput id="logonUserPassword" skinClass="skins.FormTextInput"
editable="true"
editable.logoned="false"
borderVisible="true"
borderVisible.logoned="false"
/>
</s:HGroup>
<s:HGroup width="100%" height="100%">
<s:Label width="150"/>
<s:Button id="logonButton" skinClass="skins.LogonButtonSkin"
label="ログオン"
label.logoned="ログオフ"
/>
</s:HGroup>
</s:VGroup>
</s:Skin>
最後にログオンフォームコンポーネントを使ったアプリケーションを作成します。ログオンフォームコンポーネントのlogonイベントをハンドリングして、ログオンフォームコンポーネントのisLogonedプロパティを変更してスキンステートを変更しています。
LogonFormMain.mxml
<?xml version="1.0" encoding="utf-8"?>
<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"
xmlns:forms="forms.*"
>
<forms:LogonForm id="logonForm"
skinClass="skins.LogonFormSkin"
logon="logonForm.isLogoned = true;"
logoff="logonForm.isLogoned = false;"
horizontalCenter="0"
/>
</s:Application>
画像をクリックいただくとサンプルSWFファイルが表示されます
今回のチュートリアルでは、SparkコンポーネントとSparkスキンアーキテクチャの関係について紹介しました。チュートリアルサンプルでは、Sparkコンポーネントをカスタマイズして、カスタムSparkスキンを作成することで、Sparkスキンアーキテクチャの理解を深めることができたと思います。
Flex 4は、Sparkスキンアーキテクチャを採用したことで、ロジックとデザインを分離して開発することになります。ロジックは、Flash Builder 4、デザインは、Flash CatalystやIllustratorやPhotoShopなどになり、開発ワークフローやツールも変わっていくことでしょう。