Home > FlexFramework

FlexFramework Archive

[Flex]コンポーネントのサイズがゼロになる?

Frog on AIRさんのサイトで、こんな記事がありました。

オブジェクトのサイズを取得できない(AIR β2/β3)

記事の内容は、mx.controls.Butttonのインスタンスを生成した直後に、widthとheightの値を確認すると0になってしまう。というようなものです。

実際僕も何か作っているとき、同じような状況を何度も経験しました。
ホント大ハマリして、何時間も費やしたり・・・。
自分の備忘録も兼ねて、少しこのあたりを書き残しておきます。

クラスの種類
Flex2のリファレンスで説明されているクラスの中には、
純粋なActionScript3.0のクラスとFlex2のクラスの2種類があります。

純粋なActionScript3.0のクラスとは、
Array、Number、SpriteやTextFieldなどのクラスで、
importするときのクラスパスが、flash.~のものです。

対して、Flex2のクラスとは、
UIComponent、Button、Alert、PopUpManagerなどなど、
importするときのクラスパスが、mx.~のものです。

純粋なActionScript3.0のクラスというのは、
ActionScript3.0という言語でプログラミングをする場合には基本的に使えるクラスで、
FlexBuilder2でもFlashCS3でも使えるはずです。
一方Flex2のクラスというのは、
上記純粋なActionScript3.0のクラスを拡張(継承)したクラスで、
Flex2SDKのコンパイラでは一緒に提供されているだけです。
Flex2のクラスというのは、つまり、ActionScript3.0でプログラムする場合に、
使用できるオプション的なクラス郡になります。

例えばButtonクラスならSpriteを継承したクラスFlexSprite、
をさらに継承したUIComponentクラス、
をさらに継承したものがButtonクラスです。

Flex2のクラス
Flex2のクラスはFlexフレームワーク上で動作することを前提に作成されています。
つまりいくつかのフレームワークのルールに従う必要があるということです。

このルールのひとつとしてFlex2フレームワークを用いる場合には、
多くの便利なビジュアルコンポーネントを利用できるようになる代わりに、
独自のDisplayObjectを表示させたい場合には、
UIComponentクラスを継承しなければならないというルールがあります。
(IUIComponentインターフェースでもいいのかな・・・?)

UIComponentクラスでは、
コンストラクタでは最低限のプロパティとイベントリスナの初期化しか行いません。
(このため、インスタンス生成直後にはwidthなどは0になります。)
コンポーネントがDisplayObjectのツリー上にaddChildされるたあと
initialize()メソッドが呼ばれ、描画に必要なプロパティの一部がセットされます。
その後、実際に描画されるタイミングでupdateDisplayListというメソッドで、
レイアウトやサイズ等の値が計算され(ここでwidth等の値が確定します)、描画されます。
もちろんaddChildメソッドもoverrideして、子DisplayObjectを自身に追加する際には、
子DisplayObjectのinitializeメソッドを呼び出すようにしてあります。

と、いうことで一番初めのButtonクラス生成直後にwidth、heightの値が0となる件にやっと繋がりました。Flex2のDisplayObject系のクラスでは、処理の無駄を省くため、プロパティ実際のビジュアル部分のレイアウトやサイズ等の計算は次の描画タイミング時まで保留されます。そのため、すぐにwidthやheightの値を取得することができません。

以下、対応策の案としては、

1.Flex2のクラスを使わない
元も子もないですが、使わないというのも一手ですよね。
全部flashパッケージ内から賄い、足りない部分は自分で作る、と。
ボタンくらいだったら、SimpleButtonクラスでも十分なことも多いです。

2.callLaterメソッド
UIComponentクラスで定義されているcallLaterメソッドを使います。
(参考:callLaterメソッドの使用)

callLater() メソッドは、現在の更新ではなく、次の画面更新で実行する処理をキューに格納します。

つまりcallLater(hogeMethod)を用いることで、
コンポーネントが描画される(描画用のレイアウト・サイズ計算処理などが終了した)ときに
hogeMethodが呼ばれるので、widthなどの値を取得することができます。

今回のように初期化時だけであれば、
creationCompleteイベントをハンドルしても良いかもしれませんね。

[Flex]ViewStackの画面切り替え

ViewStackを使って、画面を切り替えるにはindexかidを使うけど、
Class名を使うという手もあるよ、という記事から。

Flex cookbook beta
Selecting a view in a ViewStack just by its Class

実際に書くとこんな感じです。

< ?xml version="1.0" encoding="utf-8"?>
< mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
 < mx:Script>
  < ![CDATA[
   import mx.core.ComponentDescriptor;
   // 切り替えのスイッチになる変数
   [Bindable]
   public var actualView:Class = TitleWindow;

   private function getView(viewClass:Class):int{
    // ViewStackの持つ子画面の配列を取得
    var descriptors:Array = views.childDescriptors;
    for(var i:int=0;i
 < mx:ViewStack id="views" selectedIndex="{getView(actualView )}" width="100%" height="100%">
  < mx:TitleWindow title="TitleWindowView" width="80%" height="80%"/>
  < mx:Panel title="PanelView" width="80%" height="80%"/>
 < /mx:ViewStack>
 < mx:HBox width="100%">
  < mx:Button label="TitleWindow" click="actualView=TitleWindow"/>
  < mx:Button label="Panel" click="actualView=Panel"/>
 < /mx:HBox>
< /mx:Application>

この方法を使うと、ViewStackに積むそれぞれのViewを全部違うクラスにしなくちゃいけないので、結局id使ってちゃんとそれぞれ名前付けておくのが一番良いのかなぁ。という感じですね。
でもそれなりの規模のものを作っていて、それぞれの画面をきちんとコンポーネント化して作るような場合には結構便利なのかもしれないですね。

[Flex]ポップアップウインドウの作り方

ポップアップウインドウとか、たまに必要になるのに、そのたびに作り方忘れるのでメモ。

まずはポップアップで開くウインドウをコンポーネントとして作ります。

< ?xml version="1.0" encoding="utf-8"?>
< mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" showCloseButton="true" close="closePopup()" width="400" height="300">
  < mx:Script>
    < ![CDATA[
      import mx.managers.PopUpManager;

      private function closePopup():void{
        PopUpManager.removePopUp(this);
      }
    ]]>
  < /mx:Script>
< /mx:TitleWindow>

簡単に作るならTitleWindowがいいかな?
showCloseButtonで閉じるボタンができるので楽チンです。
closeイベントを取得して、PopupManagerから自分自身を削除するようにすると、ポップアップが消せます。

そしたらポップアップを開く側を。

< ?xml version="1.0" encoding="utf-8"?>
< mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
  < mx:Script>
    < ![CDATA[
      import mx.managers.PopUpManager;

      public function doPopup():void{
        var popup_window:PopupWindow = new PopupWindow();
        // PopupManagerにインスタンスを登録
          PopUpManager.addPopUp(popup_window, this);
        // 画面中央へ表示
          PopUpManager.centerPopUp(popup_window);
      }
    ]]>
  < /mx:Script>
  < mx:Button label="Popup" click="doPopup()"/>
< /mx:Application>

とりあえずサンプルなので、ポップアップを開くためのボタンだけ配置。この辺は適宜ですね。
addPopUpの引数は、
第一引数がwindow:IFlexDisplayObject。
ポップアップさせるインスタンス
第二引数がparent:DisplayObject。
ポップアップ表示の親となるDisplayObject。次のcenterPopUpのときの中心座標を計算する基準になるっぽい。
第三引数がmodal:Boolean = false 。
trueだと、ポップアップ表示中は、ポップアップ以外が操作不能になります。
第四引数はchildList:String = null 。
与えられる引数は以下の三種。
PopUpManagerChildList.APPLICATION
PopUpManagerChildList.PARENT
PopUpManagerChildList.POPUP

APPLICATIONはSTAGE以下に、PARENTは第二引数のparent以下に、POPUPはSystemManager以下にDisplayObjectを追加します。

他の方法としては、
centerPopUp(popUp:IFlexDisplayObject):void
このメソッドでは、クラスネームを引数で渡します(インスタンスではなく)。

ん~、リファレンスのPopupManagerの説明が微妙にわかりづらいんですよね。

[flex]任意のタイミングでAlertを消す

fxug - フォーラム

ここによると、

alert = Alert.show("hogehoge");

で開いたAlertは、

PopUpManager.removePopUp(alert);

で消せるらしいです。

確かにリファレンスを見ると、Alert.show()でAlert型が戻り値になってますね。戻り値あったんだ・・・。この関数。

というかそれ以前にAlertがPopupManagerで開かれていることを知らなかった・・・。
FlexFrameworkのソースって読めるんですよね。やっぱソースとかしっかり読んで勉強するべきなのかなぁ・・・。

Home > FlexFramework

Search
Google
Feeds
Meta
Advertisement

Return to page top