Статьи

Совместное использование макета MXML между компонентами

Больше сообщений Flex можно найти на blog.flexdevelopers.com



Часто я нахожу себя создающим визуальные компоненты, похожие по структуре и структуре. К сожалению, в отличие от свойств и методов, разметка — это сложная вещь для обмена между компонентами через традиционное объектно-ориентированное наследование.

Код макета (MXML) неизбежно дублируется в вашем приложении. Результатом является кошмар обслуживания и множество избыточного кода MXML. Обратите внимание на избыточный MXML, найденный в классах ReportEditor и ListEditor
этого приложения .
Эта статья,

вдохновленная моим коллегой
Крисом Хейеном , будет демонстрировать повторное использование кода как через наследование (свойства и методы), так и через композицию (макет), не прибегая к нежелательному использованию ActionScript для кода макета.

Композиция по наследству

В этом примере приложения у меня есть два редактора: редактор отчетов и редактор списков. Каждый разделяет общий макет, поэтому я извлеку макет в свой собственный класс — EditorLayout.

EditorLayout, написанный на MXML, будет определять макет каждого редактора. Вот мой первый проход в EditorLayout.

<!--EditorLayout.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:mx="library://ns.adobe.com/flex/mx"
         width="100%" height="100%">
    
    <fx:Script>
        <![CDATA[            
            [Bindable]
            public var title:String;
        ]]>
    </fx:Script>
    
    <mx:HBox width="100%"
             backgroundColor="#ededed"
             height="30"
             verticalAlign="middle"
             paddingRight="10"
             paddingLeft="10"
             borderStyle="solid"
             borderColor="#aab3b3">
        
        <mx:HBox id="toolBarChildrenContainer"/>
        
        <mx:Spacer width="100%"/>
        
        <mx:Label id="titleLabel"
                  color="#333333"
                  fontWeight="bold"
                  text="{title}"/>
    </mx:HBox>

    <mx:VBox id="editorChildrenContainer"
            width="100%"
            height="100%"
            minWidth="0"
            minHeight="0"/>

</mx:VBox>


Примечание. По возможности я всегда использую MXML для определения макета.
MXML для макета. ActionScript для бизнес-логики.

Хотя каждый редактор будет иметь одинаковую разметку, их содержимое будет отличаться. Для поддержки пользовательского содержимого я добавлю несколько открытых свойств, чтобы разрешить внедрение содержимого в макет. Мне также нужна пара методов, отвечающих за рендеринг этого контента. Вот мой второй проход в EditorLayout.

<!--EditorLayout.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:mx="library://ns.adobe.com/flex/mx"
         width="100%" height="100%"
         initialize="onInitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            [Bindable]
            public var title:String;
            
            private var _toolBarChildren:Array;
            private var _editorChildren:Array;
            
            public function get toolBarChildren():Array
            {
                return _toolBarChildren;
            }
            
            public function set toolBarChildren(value:Array):void
            {
                _toolBarChildren = value;
            }
            
            public function get editorChildren():Array
            {
                return _editorChildren;
            }
            
            public function set editorChildren(value:Array):void
            {
                _editorChildren = value;
            }
            
            protected function onInitialize(event:FlexEvent):void
            {
                createToolBarChildren();
                createEditorChildren();
            }
            
            protected function createToolBarChildren():void
            {
                for each (var toolBarChild:DisplayObject in _toolBarChildren)
                {
                    if (!toolBarChildrenContainer.contains(toolBarChild))
                    {
                        toolBarChildrenContainer.addChild(toolBarChild);
                    }
                }
            }
            
            protected function createEditorChildren():void
            {
                for each (var editorChild:DisplayObject in _editorChildren)
                {
                    if (!editorChildrenContainer.contains(editorChild))
                    {
                        editorChildrenContainer.addChild(editorChild);
                    }
                }
            }
        ]]>
    </fx:Script>
    
    <mx:HBox width="100%"
             backgroundColor="#ededed"
             height="30"
             verticalAlign="middle"
             paddingRight="10"
             paddingLeft="10"
             borderStyle="solid"
             borderColor="#aab3b3">
        
        <mx:HBox id="toolBarChildrenContainer"/>
        
        <mx:Spacer width="100%"/>
        
        <mx:Label id="titleLabel"
                  color="#333333"
                  fontWeight="bold"
                  text="{title}"/>
    </mx:HBox>

    <mx:VBox id="editorChildrenContainer"
            width="100%"
            height="100%"
            minWidth="0"
            minHeight="0"/>

</mx:VBox>

Мои редакторы также будут использовать некоторые свойства и методы, поэтому я создам базовый класс для использования традиционного объектно-ориентированного наследования.

<!--Editor.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel 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:comps="comps.*"
          title="{editorType} Editor"
          width="500"
          paddingTop="10"
          paddingBottom="10"
          paddingLeft="10"
          paddingRight="10">
    
    <fx:Script>
        <![CDATA[
            public static const REPORT:String = "Report";
            public static const LIST:String = "List";
            
            [Bindable]
            public var editorType:String;

            protected function save():void
            {
                throw new Error("Abstract Class - override this method in a subclass.");
            }
        ]]>
    </fx:Script>
    
</mx:Panel>

Наконец, я создам свои классы редактора. Класс редактора расширит базовый класс Editor и будет состоять из экземпляра EditorLayout. Пользовательское содержимое вводится в общий макет декларативно с использованием MXML.

<!--ReportEditor.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor 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:comps="comps.*" 
              preinitialize="onPreinitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }
            
            protected function publishButton_clickHandler(event:MouseEvent):void
            {
                publish();
            }
            
            override protected function save():void
            {
                //save
            }
            
            protected function publish():void
            {
                //publish
            }

            protected function onPreinitialize(event:FlexEvent):void
            {
                editorType = Editor.REPORT;
            }

        ]]>
    </fx:Script>
    
    
    <comps:EditorLayout title="{editorType}">
        
        <comps:toolBarChildren>
            
            <s:Button id="saveButton" 
                      label="Save"
                      click="saveButton_clickHandler(event)"/>
            
            <s:Button id="publishButton" 
                      label="Publish"
                      click="publishButton_clickHandler(event)"/>
            
        </comps:toolBarChildren>
        
        <comps:editorChildren>
            
            <comps:ReportForm id="reportForm"/>
            
        </comps:editorChildren>
        
    </comps:EditorLayout>
    
</comps:Editor>

<!--ListEditor-->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor 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:comps="comps.*" 
              preinitialize="onPreinitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }
            
            protected function submitButton_clickHandler(event:MouseEvent):void
            {
                submit();
            }
            
            override protected function save():void
            {
                //save
            }
            
            protected function submit():void
            {
                //submit
            }
            
            protected function onPreinitialize(event:FlexEvent):void
            {
                editorType = Editor.LIST;
            }
        ]]>
    </fx:Script>
    
    
    <comps:EditorLayout title="{editorType}">
        
        <comps:toolBarChildren>
            
            <s:Button id="saveButton" 
                      label="Save"
                      click="saveButton_clickHandler(event)"/>
            
            <s:Button id="submitButton" 
                      label="Submit"
                      click="submitButton_clickHandler(event)"/>
            
        </comps:toolBarChildren>
        
        <comps:editorChildren>
            
            <comps:ListForm id="listForm"/>
            
        </comps:editorChildren>
        
    </comps:EditorLayout>
    
</comps:Editor>

Конечный результат (см. Ниже) — более чистый код во всем приложении (без кода макета ActionScript и избыточного кода MXML).
Просмотр включена
Конечно, лучше метод предполагает использование скиннинга архитектуры Flex 4 в. Оставайтесь в курсе.
Обновление: см. Запись в блоге под названием «
Flex Skinning для общего использования ».