2010-10-18 1 views
9

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

<!-- 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 внутри компонент приложения показывает пустую панель с кнопкой «Go для редактирования». Но когда я нажимаю на него, кнопка «Extra edit» становится видимой, но кнопка «Show in edit» внутри панели управления не работает.

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

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

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

+0

+1 для хорошо сформулированных вопрос, с образцами. – JeffryHouser

ответ

2

Предлагаю вам использовать скин-архитектуру 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

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

+1

Добавление в CustomAdvancedPanel без повторного использования состояний возвращает три состояния из AdvancedPanel (нормальное, редактировать, отключено). . Вы не можете использовать их в mxml, потому что компилятор говорит, что их нет, но они представлены в виде набора данных. – Treur

0

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

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

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

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

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

states = xyz 

, который в свою очередь, является AS эквивалент говоря

setStates(xyz) 

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

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

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

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

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

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

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> 
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); 
       } 
      } 
     } 

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