Flex. Понимание itemRenderer'ов. Часть 2. Внешние рендереры

Часть 2. Внешние рендереры

В первой части я показывал Вам, как сделать действующий itemRenderer - то есть, itemRenderer, тэги MXML которого и код ActionScript находятся в том же самом файле, где и список, использующий itemRenderer.

Вы также вспомните, о чём я говорил, что Вы должны думать о встроенных itemRenderer'ах, являющихся отдельными классами. Компилятор Flex фактически извлекает встроенный код и делает для Вас класс. Выгода встроенных itemRenderer'ов - та, что код находится в том же самом месте, где и список, но это - также недостаток, когда itemRenderer становится сложным. То, что я собираюсь показать Вам в этой статье, это то, как сделать класс самостоятельно. Извлечение itemRenderer'а во внешний файл имеет несколько выгод:

MXML itemRenderer

В части 1 Вы видели, что был комплексный itemRenderer используемый для DataGrid:

<mx:DataGridColumn headerText="Title" dataField="title">
    <mx:itemRenderer>
        <mx:Component>
            <mx:HBox paddingLeft="2">
                <mx:Script>
                    <![CDATA[
                        public function set data(value:Object):void {
                            super.data = value;
                            var today:Number = (new Date()).time;
                            var pubDate:Number = Date.parse(data.date);
                            if(pubDate > today) setStyle("backgroundColor",0xff99ff);
                            else setStyle("backgroundColor",0xffffff);
                        }
                    ]]>
                </mx:Script>
                <mx:Image source="{data.image}" width="50" height="50" scaleContent="true"/>
                <mx:Text width="100%" text="{data.title}"/>
            </mx:HBox>
        </mx:Component>
    </mx:itemRenderer>
</mx:DataGridColumn>

itemRenderer основанный на HBox, содержит Image и Text, и цвет фона установлен согласно полю pubDate записи элементов. Вы можете написать тот же самый itemRenderer как внешний файл, используя эти шаги:

  1. Если Вы используете Flex Builder, создайте новый файл MXML Component (я назвал источник GridColumnSimpleRenderer, но используйте то, что Вам нравится), и задайте корневой тэг HBox. Не волнуйтесь о размере.
  2. Если Вы используете только SDK, создайте новый файл MXML (назовите его GridColumnSimpleRenderer.mxml), и задайте корневой тэг HBox.
  3. Когда файл открыт, скопируйте всё между <mx:HBox> и </mx:HBox>, но не копируйте эти тэги, так как они уже находятся в файле. Результат должен выглядеть так:
    <?xml version="1.0" encoding="utf-8"?>
    <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
        <mx:Script>
            <![CDATA[
                override public function set data(value:Object):void {
                    super.data = value;
                    var today:Number = (new Date()).time;
                    var pubDate:Number = Date.parse(data.date);
                    if(pubDate > today) setStyle("backgroundColor",0xff99ff);
                    else setStyle("backgroundColor",0xffffff);
                }
    	]]>
        </mx:Script>
        <mx:Image source="{data.image}" width="50" height="50" scaleContent="true"/>
        <mx:Text width="100%" text="{data.title}"/>
    </mx:HBox>
    
  4. Сохраните файл

Теперь измените определение DataGridColumn, удалив встроенный itemRenderer, и замените его этим:

<mx:DataGridColumn headerText="Title" dataField="title"
itemRenderer="GridColumnSimpleRenderer">

Теперь запустите приложение. Вы получите сюрприз. Сюрприз состоит в том, насколько высоки строки. Это из-за присутствия height="300" в itemRenderer'е.

Определение ширины и высоты itemRenderer'ов

Контрол List всегда устанавливает ширину itemRenderer'а. В этом примере проигнорирована явная height="400". Вы должны написать свой itemRenderer, чтобы изменить ширину, как пользователь изменяет ширина списка или столбца.

Высота это другое дело. Если у списка будет явная настройка rowHeight, то она наложит эту высоту на каждую строку, игнорируя любую высоту, которую Вы установили в itemRenderer'е. Однако, если Вы установите свойство списка variableRowHeight в true, то тогда список примет высоту itemRenderer'а. В этом примере высота явно установлена в 300, таким образом каждая строка в 300 пикселей высотой.

Чтобы исправить это, удалите явную высоту из файла itemRenderer'а, и приложение будет работать правильно.

Динамическое изменение itemRenderer'а

В этом примере set data() функция была перезаписана, чтобы проверить данные и установить свойство backgroundColor itemRenderer'а. Это очень распространено. Перезапись set data() дает возможность Вам перехватывать время, когда данные изменяются для новой строки, и Вы можете сделать изменения стиля.

Частые ошибки:

ActionScript itemRenderer

Теперь Вы напишете другой itemRenderer, на сей раз используя класс ActionScript.

<mx:itemRenderer>
    <mx:Component>
        <mx:HBox verticalAlign="top">
            <mx:Image source="{data.image}"/>
            <mx:VBox height="115" verticalAlign="top" verticalGap="0">
                <mx:Text text="{data.title}" fontWeight="bold" width="100%"/>
                <mx:Spacer height="20"/>
                <mx:Label text="{data.author}"/>
                <mx:Label text="Available {data.date}"/>
                <mx:Spacer height="100%"/>
                <mx:HBox width="100%" horizontalAlign="right">
                    <mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]">
                        <mx:click>
                            <![CDATA[
                                var e:BuyBookEvent = new BuyBookEvent();
                                e.bookData = data;
                                dispatchEvent(e);
                            ]]>
                        </mx:click>
                    </mx:Button>
                </mx:HBox>
            </mx:VBox>
        </mx:HBox>
    </mx:Component>
</mx:itemRenderer>

Превратите его во внешний ActionScript itemRenderer. Для этого Вам необходимо сделать эти шаги:

  1. Создайте новый класс ActionScript. Назовите его BookTileRenderer.as и заставьте его расширить HBox, точно так же как встроенный itemRenderer.
    package
    {
        import flash.events.MouseEvent;
        import mx.containers.HBox;
        import mx.containers.VBox;
        import mx.controls.Button;
        import mx.controls.Image;
        import mx.controls.Label;
        import mx.controls.Spacer;
        import mx.controls.Text;
    
        public class BookTileRenderer extends HBox
        {
            public function BookTileRenderer()
            {
                super();
            }
        }
    }
    
  2. Создайте переменные, чтобы держать ссылки на дочерние компоненты.
            private var coverImage:Image;
            private var titleText:Text;
            private var spacer1:Spacer;
            private var authorLabel:Label;
            private var pubdateLabel:Label;
            private var spacer2:Spacer;
            private var buyButton:Button;
    
  3. Пререгрузите функцию createChildren(), чтобы создать дочерние компоненты и добавить их к HBox.
            override protected function createChildren():void
            {
                coverImage = new Image();
                addChild(coverImage);
    			
                var innerBox:VBox = new VBox();
                innerBox.explicitHeight = 115;
                innerBox.percentWidth = 100;
                innerBox.setStyle("verticalAlign","top");
                innerBox.setStyle("verticalGap", 0);
                addChild(innerBox);
    
                    titleText = new Text();
                    titleText.setStyle("fontWeight","bold");
                    titleText.percentWidth = 100;
                    innerBox.addChild(titleText);
    
                    spacer1 = new Spacer();
                    spacer1.explicitHeight = 20;
                    innerBox.addChild(spacer1);
    
                    authorLabel = new Label();
                    innerBox.addChild(authorLabel);
    
                    pubdateLabel = new Label();
                    innerBox.addChild(pubdateLabel);
    
                    spacer2 = new Spacer();
                    spacer2.percentHeight = 100;
                    innerBox.addChild(spacer2);
    
                    var buttonBox:HBox = new HBox();
                    buttonBox.percentWidth = 100;
                    buttonBox.setStyle("horizontalAlign","right");
                    innerBox.addChild(buttonBox);
    
                        buyButton = new Button();
                        buyButton.label = "Buy";
                        buyButton.setStyle("fillColors",[0x99ff99,0x99ff99]);
                        buyButton.addEventListener(MouseEvent.CLICK,handleBuyClick);
                        buttonBox.addChild(buyButton);
            }
    
    Я выровнял код, чтобы показать родительско-дочерние отношения. Кроме того, удостоверьтесь, что Вы включили слушатель события на кнопку Buy.
  4. Перегрузите функцию commitProperties(), и установите контролы пользовательского интерфейса из данных.
            override protected function commitProperties():void
            {
                super.commitProperties();
    
                coverImage.source = data.image;
                titleText.text = data.title;
                authorLabel.text = data.author;
                pubdateLabel.text = data.date;
            }
    
  5. Добавьте обработчика событий для кнопки Buy.
            private function handleBuyClick(event:MouseEvent):void
            {
                var e:BuyBookEvent = new BuyBookEvent();
                e.bookData = data;
                dispatchEvent(e);
            }
    
  6. Измените TileList в главном приложении, чтобы использовать itemRenderer класса ActionScript. Просто удалите inlineItemRenderer и замените его свойство itemRenderer справа в тэге.
    <mx:TileList id="mylist" x="29" y="542" width="694" itemRenderer="BookTileRenderer"
        dataProvider="{testData.book}" height="232" columnWidth="275" rowHeight="135">
    

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

itemRenderers многократного использования

Вот пример itemRenderer'а который отображает числовое значение, используя CurrencyFormatter. Я называю это PriceFormatter:

<?xml version="1.0" encoding="utf-8"?>
<mx:Text xmlns:mx="http://www.adobe.com/2006/mxml">

    <mx:Script>
        <![CDATA[
            import mx.controls.dataGridClasses.DataGridListData;

            [Bindable] private var formattedValue:String;

            override public function set data(value:Object):void
            {
                super.data = value;
                formattedValue = cfmt.format(Number(data[(listData as DataGridListData).dataField]));
            }
        ]]>
    </mx:Script>
    <mx:CurrencyFormatter precision="2" id="cfmt" />
    <mx:text>{formattedValue}</mx:text>
</mx:Text>

Ключ к этому itemRenderer'у показан красным, устанавливая bindable переменную formattedValue. Во-первых, Вы увидете, что <mx:CurrentFormatter> был определён как тэг MXML (Если Вы предпочитаете, Вы можете это также сделать в ActionScript.) с id в cfmt. В примере выше, formattedValue установлен в результат запроса функции format() CurrentFormatter'а.

Функция берёт Number как свой тип параметра, таким образом значение приведено к Number - поэтому, dataProvider для списка - XML, и все в XML - текст; если Вы будете использовать Object для своих данных, и у Вас есть действительные числовые значения, то делая приведение, Number будет безопасен.

Как Вы знаете, данные - свойство, которое содержит элемент, отображается itemRenderer'ом. Использование нотации [ ], это другой способ обратиться к полям данных элемента. Например, data['price'] были бы ценовым столбцом. Но чтобы сделать этот itemRenderer многократного использования, Вы не можете закодировать для указанного поля, таким образом необходим более универсальный путь.

Это - тот, куда входит listData. Все Flex-компоненты, у которых есть интерфейс IDropInListItemRenderer, имеют свойство listData.

Примечание. Большинство контролов, таких как Text, Label, Button, CheckBox, и т.д, имеют IDropInListItemRenderer. Большинство контейнеров, таких как HBox, Canvas и т.д., не обладают этим интерфейсом. Если Вы хотите использовать listData в itemRenderer'е, который расширяет Container, Вы должны будете самостоятельно осуществить IDropInListItemRenderer; я расскажу об этом в следующей главе.

listData, данный itemRenderer'у, содержит, между прочим, rowIndex и контрол, которому принадлежит itemRenderer - DataGrid, List или TileList. Когда у Вас есть itemRenderer, используемый для DataGrid, listData, это фактически объект DataGridListData, который включает в себя columnIndex и dataField, связанные с DataGridColumn. Вот прерывание оператора приведённого выше, начинающееся изнутри:

Заключение

Выберите то, что для Вас удобно, когда используете itemRenderer'ы. Некоторые люди только работают в ActionScript, который является хорошим, когда у Вас есть опыт с Flex и ActionScript. MXML также делает быстро работу простых itemRenderer'ов.

Источник


<-Назад Вперёд->