9

controlBarContent を持つ AdvancedPanel という次のパネル コンポーネントがあります。

<!-- AdvancedPanel.mxml -->
<s:Panel>
  <s:states>
    <s:State name="normal" />
    <s:State name="edit" />
  </s:states>
  <s:controlBarContent>
    <s:Button 
      includeIn="edit"
      label="Show in edit"
      />
    <s:Button 
      label="Go to edit"
      click="{currentState='edit'}"
      />
  </s:controlBarContent>
</s:Panel>

controlBarContent を再宣言したくないので、AdvancedPanel に基づいて CustomAdvancedPanel と呼ばれる 2 番目のパネルを作成しました。

<!-- CustomAdvancedPanel.mxml -->
<local:AdvancedPanel>
  <s:Button includeIn="edit" label="Extra edit button" />
</local:AdvancedPanel>

CustomAdvancedPanel の「編集」状態がコンパイラに従って宣言されていないため、これは機能しません。次のように、CustomAdvancedPanel.mxml で編集状態を再宣言する必要があります。

  <!-- CustomAdvancedPanel.mxml with edit state redeclared -->
    <local:AdvancedPanel>
      <local:states>
        <s:State name="normal" />
        <s:State name="edit" />
      </local:states>
      <s:Button includeIn="edit" label="Extra edit button" />
    </local:AdvancedPanel>

アプリケーション コンポーネント内で CustomAdvancedPanel を使用すると、[編集に移動] ボタンのある空のパネルが表示されます。しかし、クリックすると「追加編集ボタン」が表示されますが、controlBar 内の「編集で表示」ボタンは表示されません。

CustomAdvancedPanel が空の場合、再宣言された状態と「追加の編集ボタン」がなければ、パネルは正常に機能します。

AdvancedPanel で宣言されている State オブジェクトが CustomAdvancedPanel と同じではないため、同じ名前であっても状態が異なるためだと思います。でも。mxml で (再) 宣言しないと、CustomAdvancedPanel 内で AdvancedPanel の状態を使用できません。

この種の状態の再利用を実現する方法はありますか? または、同じ結果を得るためのより良い方法はありますか?

4

6 に答える 6

2

Sparkのスキニングアーキテクチャを使用して目標を達成することをお勧めします。スキンの状態はホストコンポーネントに継承されるため、すべてのロジックをOOP方式で配置できます。ただし、スキンには重複コードが含まれます:(とにかく、すべてのコンポーネントの重複コードよりも優れています。

したがって、AdvancedPanelは次のようになります。

package
{
    import flash.events.MouseEvent;

    import spark.components.supportClasses.ButtonBase;
    import spark.components.supportClasses.SkinnableComponent;

    [SkinState("edit")]
    [SkinState("normal")]
    public class AdvancedPanel extends SkinnableComponent
    {
        [SkinPart(required="true")]
        public var goToEditButton:ButtonBase;
        [SkinPart(required="true")]
        public var showInEditButton:ButtonBase;

        private var editMode:Boolean;

        override protected function getCurrentSkinState():String
        {
            return editMode ? "edit" : "normal";
        }

        override protected function partAdded(partName:String, instance:Object):void
        {
            super.partAdded(partName, instance);
            if (instance == goToEditButton)
                goToEditButton.addEventListener(MouseEvent.CLICK, onGoToEditButtonClick);
        }

        override protected function partRemoved(partName:String, instance:Object):void
        {
            super.partRemoved(partName, instance);
            if (instance == goToEditButton)
                goToEditButton.removeEventListener(MouseEvent.CLICK, onGoToEditButtonClick);
        }

        private function onGoToEditButtonClick(event:MouseEvent):void
        {
            editMode = true;
            invalidateSkinState();
        }
    }
}

そしてCustomAdvancedPanelの場合:

package
{
    import spark.components.supportClasses.ButtonBase;

    public class CustomAdvancedPanel extends AdvancedPanel
    {
        [SkinPart(required="true")]
        public var extraEditButton:ButtonBase;
    }
}

もちろん、Panelクラスから継承することもできますが、サンプルコードをより単純にしました。

そしてスキン:

<?xml version="1.0" encoding="utf-8"?>
<!-- AdvancedPanelSkin.mxml -->
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">
    <fx:Metadata>
        [HostComponent("AdvancedPanel")]
    </fx:Metadata>
    <s:states>
        <s:State name="normal" />
        <s:State name="edit" />
    </s:states>
    <s:Panel left="0" right="0" top="0" bottom="0">
        <s:controlBarContent>
            <s:Button id="showInEditButton" label="Show in edit" includeIn="edit" />
            <s:Button id="goToEditButton" label="Go to edit" />
        </s:controlBarContent>
    </s:Panel>
</s:Skin>

と:

<?xml version="1.0" encoding="utf-8"?>
<!-- CustomAdvancedPanelSkin.mxml -->
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">
    <fx:Metadata>[HostComponent("CustomAdvancedPanel")]</fx:Metadata>
    <s:states>
        <s:State name="normal" />
        <s:State name="edit" />
    </s:states>
    <s:Panel left="0" right="0" top="0" bottom="0">
        <s:Button includeIn="edit" label="Extra edit button" id="extraEditButton" />
        <s:controlBarContent>
            <s:Button id="showInEditButton" label="Show in edit" includeIn="edit" />
            <s:Button id="goToEditButton" label="Go to edit" />
        </s:controlBarContent>
    </s:Panel>
</s:Skin>
于 2011-03-30T15:33:31.167 に答える
1

私の知る限り、コンポーネントの状態は継承されたコンポーネントにクロスオーバーしません。考えてみてください - その場合 (状態を継承できる場合) は、コンポーネントを拡張したいときはいつでも非常に複雑になります。継承されたすべての状態を認識し、つま先を踏まないようにする必要があります。

于 2010-10-18T23:04:01.910 に答える
0

「それとも、同じ結果を得るより良い方法はありますか?」

あなたが尋ねたので、そしてあなたが追加のCustomAdvancedPanelコンポーネントの必要性について明確なケースを作らなかったので、AdvancedPanelコンポーネントに「追加の編集ボタン」を置くことは最も簡単な解決策です。

<!-- AdvancedPanel.mxml -->
<s:Panel>
  <s:states>
    <s:State name="normal"/>
    <s:State name="edit"/>
  </s:states>
  <s:Button includeIn="edit" label="Extra edit button"/>
  <s:controlBarContent>
    <s:Button 
      includeIn="edit"
      label="Show in edit"/>
    <s:Button 
      label="Go to edit"
      click="{currentState='edit'}"/>
  </s:controlBarContent>
</s:Panel>
于 2010-11-09T09:03:18.673 に答える
0

オブジェクト指向プログラミングの制限だと思いますが、正確にはわかりません。私は Flex の専門家ではありませんが、オブジェクト指向プログラミングの観点から考えてみました。

最初に、オブジェクトを作成すると、Flex (または任意の OO 言語) が自動的にそのオブジェクトのコピーとその親オブジェクトのプライベート コピーを作成し、次にその親オブジェクトのプライベート コピーを作成し、オブジェクト全体を同様に作成することを考慮してください。木。奇妙に聞こえるかもしれませんが、この例として、コンストラクターで super() を記述すると、親クラスのコンストラクターを呼び出すことになります。

Flex には「プロパティ」と呼ばれるものがあります。これは、Java のプライベート メンバー フィールド (変数) にパブリックの getter メソッドと setter メソッドを使用することに相当します。宣言すると

<local:states>xyz</local:states>

あなたは効果的に言っています

states = xyz

これは、次のように言うのに相当する AS です。

setStates(xyz)

重要な部分は、これはプロパティに関する一般的な規則ですが、setStates はパブリック メソッドであり、誰でもこれを呼び出すことができるということです。ただし、states 配列自体は非公開です。宣言しない場合、CustomAdvancedPanel には states プロパティがありません。setStates または getStates メソッドもありません。ただし、setStates/getStates は public であるため、AdvancedPanel から継承されるため、これらのメソッドがあるかのように機能します。これらのメソッドの 1 つを呼び出す (states 配列を取得または設定する) と、実際には、親オブジェクトである AdvancedPanel にあるメソッドが存在するメソッドが呼び出されます。AdvancedPanel がメソッドを実行すると、AdvancedPanel 自体の states 配列の値読み取りまたは設定されます。これが、CustomAdvancedPanel で状態を再宣言しないと、すべてが完全に機能する理由です。CustomAdvancedPanel で状態配列を設定および取得していると思いますが、実際には舞台裏で AdvancedPanel 親オブジェクトの状態配列を操作しています。完全に元気で良い。

ここで、CustomAdvancedPanel の states 配列を再定義します - 何が起こっているのでしょうか? Flex でプロパティを宣言することは、プライベート クラス レベル変数とパブリック ゲッターおよびセッターを宣言することに似ていることに注意してください。そのため、CustomAdvancedPanel に states と呼ばれるプライベート配列と、その配列を取得/設定するためのパブリック getter および setter を与えています。これらのゲッターとセッターは、AdvancedPanel のものをオーバーライドします。したがって、アプリケーションは同じ方法で CustomAdvancedPanel とやり取りしますが、舞台裏では、AdvancedPanel のメソッド/変数を操作するのではなく、CustomAdvancedPanel 自体で宣言したものを操作します。これは、CustomAdvancedPanel の状態を変更すると、AdvancedPanel から継承された部分が反応しない理由を説明しています。その表示は AdvancedPanel の states 配列にリンクされているためです。

では、状態を再宣言しない基本的な例で includeIn が許可されないのはなぜですか? 知らない。それはバグであるか、おそらく動作しない正当な言語/オブジェクト指向の理由があります。

私の説明が完全に正確ではない可能性があります。それは私が物事を理解している限りです。問題のボタンがスーパークラスの一部であることを考えると、なぜそれが実際に起こるのか、私自身はわかりません。いくつかの興味深いテストは次のとおりです。

  1. クリック ハンドラーをインラインではなく実際のパブリック メソッドに移動します。
  2. クリック ハンドラーに super.currentState='edit' を追加します。

このすべての継承について詳しく知りたい場合は、あるクラスが別のクラスを継承する単純なクラスを ActionScript または Flex で記述し、さまざまな関数呼び出しを実行して何が起こるかを確認してください。

于 2010-10-20T15:28:40.333 に答える
0

もちろん、政治的に正しい方法は、スキンを使用することです。ただし、MXML クラスの状態継承を力ずくで行いたいだけの場合は、回避策を見つけました。

このメソッドが機能するためには、拡張 MXML クラスが基本 MXML クラスとまったく同じ状態を宣言し、それ以上でもそれ以下でもなく、すべて同じ名前で宣言する必要があります。

次に、拡張クラスに次のメソッドを挿入します。

        override public function set states(value:Array):void
        {
            if(super.states == null || super.states.length == 0)
            {
                super.states  = value;

                for each (var state:State in value)
                {
                    state.name = "_"+state.name;
                }
            }
            else
            {
                for each (var state:State in value)
                {
                    state.basedOn = "_"+state.name;
                    super.states.push(state);
                }
            }
        }

これが機能するのは、コンポーネントが作成されるときに、states 変数が基本クラスによって 1 回、拡張クラスによって 1 回、2 回設定されるためです。この回避策は、それらを組み合わせるだけです。

于 2013-10-27T16:07:00.420 に答える
0

Assaf Lavie の言うとおりです。カスタム コンポーネントに親の状態があると、非常に混乱します。スキンの使用を検討してください:

于 2011-04-11T05:46:59.103 に答える