Как наследовать состояния с помощью mxml? - PullRequest
9 голосов
/ 18 октября 2010

У меня есть следующий компонент панели под названием AdvancedPanel с controlBarContent:

<!-- 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>

Я создал вторую панель под названием CustomAdvancedPanel на основе AdvancedPanel, так как я не хочу переопределять controlBarContent

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

Это не работает, потому что состояние 'edit' в 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 внутри компонента приложения показывает пустую панель с кнопкой «Перейти к редактированию».Но когда я нажимаю на нее, «Дополнительная кнопка редактирования» становится видимой, а кнопка «Показать в редактировании» внутри панели управления - нет.

Когда CustomAdvancedPanel пуст, без переобъявленных состояний и «Дополнительная кнопка редактирования»"панель работает просто отлично.

Я думаю, это потому, что объект State, объявленный в AdvancedPanel, отличается от CustomAdvancedPanel, поэтому состояние отличается, даже если они имеют одинаковые имена.Тем не мение.Я не могу использовать состояния AdvancedPanel внутри CustomAdvancedPanel без (повторного) объявления их в mxml.

Есть ли способ достичь такого повторного использования состояний?Или есть лучший способ получить тот же результат?

Ответы [ 6 ]

2 голосов
/ 30 марта 2011

Я предлагаю вам использовать скиннинг-архитектуру Spark для достижения ваших целей. Поскольку состояния скина наследуются в компоненте хоста, вы можете разместить всю логику в ООП. Но скины по-прежнему будут содержать повторяющийся код :( В любом случае, это лучше, чем дублирующий код всех компонентов.

Таким образом, наша 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>
1 голос
/ 19 октября 2010

AFAIK Состояние компонента не переходит к унаследованным компонентам.Подумайте об этом - если бы это было так (если бы вы могли наследовать состояния), это сделало бы жизнь действительно сложной, если бы вы захотели расширить компонент;Вы должны знать обо всех унаследованных состояниях и не наступать на их пальцы.

0 голосов
/ 27 октября 2013

Конечно, политически корректным способом является использование скинов. Однако для тех, кто действительно хочет просто перебрать принудительное наследование состояний для классов 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);
                }
            }
        }

Это работает, потому что при создании компонента переменная состояний устанавливается дважды, один раз базовым классом и один раз расширяющим классом. Этот обходной путь просто объединяет их.

0 голосов
/ 11 апреля 2011

Ассаф Лави прав, было бы очень запутанно, если бы у пользовательского компонента были состояния его родителя.я бы сказал, подумайте об использовании скинов:

0 голосов
/ 09 ноября 2010

«Или есть лучший способ получить тот же результат?»

Поскольку вы спросили, и поскольку вы не дали четкого объяснения необходимости дополнительного компонента 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>
0 голосов
/ 20 октября 2010

Я считаю, что это ограничение ОО-программирования, но не уверен, что именно. Я не эксперт по Flex, но я думал об этом с точки зрения объектно-ориентированного программирования, и вот что я думаю:

Сначала учтите, что при создании объекта Flex (или любой язык OO) автоматически создает копию этого объекта И частную копию своего родительского объекта, который, в свою очередь, создает личную копию своего родительского объекта и т. Д. все дерево объектов. Это может звучать странно, но в качестве примера, когда вы пишете super () в конструкторе, вы вызываете конструктор родительского класса.

Flex имеет то, что он называет «свойствами». Это эквивалентно тому, что в Java было бы частным полем (переменной) члена с общедоступным методом получения и установки. Когда вы объявляете

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

Вы фактически говорите

states = xyz

что, в свою очередь, эквивалентно высказыванию

setStates(xyz)

Важная часть, и это общее правило о свойствах, заключается в том, что setStates - это публичный метод, любой может вызвать его. Однако сам массив состояний является частным. Если вы не объявите один, CustomAdvancedPanel не имеет свойства состояний. Он также не имеет метода setStates или getStates. Однако, поскольку setStates / getStates являются общедоступными, они наследуют их от AdvancedPanel, поэтому он функционирует так, как если бы он имел эти методы. Когда вы вызываете один из этих методов (получить или установить массив состояний), он на самом деле вызывает метод , где он существует , который находится в его родительском объекте AdvancedPanel. Когда AdvancedPanel выполняет метод, значение массива состояний в самой AdvancedPanel считывается или устанавливается. Вот почему, когда вы не переоцениваете какие-либо состояния в CustomAdvancedPanel, все работает отлично - вы думаете, что устанавливаете и получаете массив состояний в CustomAdvancedPanel, но на самом деле за кулисами вы работаете с массивом состояний в родительском объекте AdvancedPanel, прекрасно и хорошо.

Теперь вы переопределяете массив состояний в CustomAdvancedPanel - что происходит? Помните, что объявление свойства во Flex - это то же самое, что объявление частной переменной уровня класса и общедоступных методов получения и установки. Таким образом, вы предоставляете CustomAdvancedPanel частный массив, называемый состояниями и общедоступными методами получения и установки этого массива. Эти геттеры и сеттеры будут переопределять те из AdvancedPanel. Так что теперь ваше приложение будет взаимодействовать с CustomAdvancedPanel таким же образом, но за кулисами вы больше не работаете с методами / переменными AdvancedPanel, а с теми, которые вы объявили в самом CustomAdvancedPanel. Это объясняет, почему при изменении состояния CustomAdvancedPanel часть, унаследованная от AdvancedPanel, не реагирует, поскольку ее отображение связано с массивом состояний в AdvancedPanel, который все еще существует независимо.

Так почему же includeIn не разрешен в базовом примере, где вы не переопределяете состояния? Я не знаю. Либо это ошибка, либо, что более вероятно, существует легитимная языковая / OO причина, по которой он никогда не сможет работать.

Возможно, мое объяснение не совсем точно. Это насколько я понимаю. Я сам не знаю, почему это действительно произойдет, учитывая, что рассматриваемая кнопка является частью суперкласса. Пара интересных тестов будет:

  1. переместить обработчик щелчка в настоящий открытый метод вместо встроенного.
  2. добавить super.currentState = 'edit' в обработчик кликов.

Если вы хотите узнать больше обо всем этом наследовании, напишите несколько простых классов в ActionScript или Flex, где один класс наследует другой, и запустите различные вызовы функций, чтобы увидеть, что происходит.

...