В первой части я показывал Вам, как сделать действующий itemRenderer - то есть, itemRenderer, тэги MXML которого и код ActionScript находятся в том же самом файле, где и список, использующий itemRenderer.
Вы также вспомните, о чём я говорил, что Вы должны думать о встроенных itemRenderer'ах, являющихся отдельными классами. Компилятор Flex фактически извлекает встроенный код и делает для Вас класс. Выгода встроенных itemRenderer'ов - та, что код находится в том же самом месте, где и список, но это - также недостаток, когда itemRenderer становится сложным. То, что я собираюсь показать Вам в этой статье, это то, как сделать класс самостоятельно. Извлечение 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 как внешний файл, используя эти шаги:
HBox
. Не волнуйтесь о размере.
HBox
.
<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>
Теперь измените определение DataGridColumn, удалив встроенный itemRenderer, и замените его этим:
<mx:DataGridColumn headerText="Title" dataField="title" itemRenderer="GridColumnSimpleRenderer">
Теперь запустите приложение.
Вы получите сюрприз.
Сюрприз состоит в том, насколько высоки строки.
Это из-за присутствия height="300"
в itemRenderer'е.
Контрол List всегда устанавливает ширину itemRenderer'а.
В этом примере проигнорирована явная height="400"
.
Вы должны написать свой itemRenderer, чтобы изменить ширину, как пользователь изменяет ширина списка или столбца.
Высота это другое дело.
Если у списка будет явная настройка rowHeight
, то она наложит эту высоту на каждую строку, игнорируя любую высоту, которую Вы установили в itemRenderer'е.
Однако, если Вы установите свойство списка variableRowHeight
в true
, то тогда список примет высоту itemRenderer'а.
В этом примере высота явно установлена в 300, таким образом каждая строка в 300 пикселей высотой.
Чтобы исправить это, удалите явную высоту из файла itemRenderer'а, и приложение будет работать правильно.
В этом примере set data()
функция была перезаписана, чтобы проверить данные и установить свойство backgroundColor
itemRenderer'а.
Это очень распространено.
Перезапись set data()
дает возможность Вам перехватывать время, когда данные изменяются для новой строки, и Вы можете сделать изменения стиля.
Частые ошибки:
super.data = value;
. Этот VITAL-отказ действительно испортит Ваш itemRenderer.
pubDate
находится в будущем, но Вы должны помнить, что itemRenderer'ы являются циклическими, и таким образом оператор else
очень необходим.
Теперь Вы напишете другой 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. Для этого Вам необходимо сделать эти шаги:
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(); } } }
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;
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.
commitProperties()
, и установите контролы пользовательского интерфейса из данных.
override protected function commitProperties():void { super.commitProperties(); coverImage.source = data.image; titleText.text = data.title; authorLabel.text = data.author; pubdateLabel.text = data.date; }
private function handleBuyClick(event:MouseEvent):void { var e:BuyBookEvent = new BuyBookEvent(); e.bookData = data; dispatchEvent(e); }
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, и, говорю весьма искренне, это небольшая выгода в производительности.
Вот пример 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.
Вот прерывание оператора приведённого выше, начинающееся изнутри:
listData as DataGridListData
- Приводит listData к объекту DataGridListData, таким образом у Вас есть доступ к его dataField.
.dataField
- Поле для колонки, которая будет рендериться. Это то, что делает это itemRenderer общим. Вы можете использовать этот itemRenderer для множества столбцов. В этом примере dataField это 'price'.
data[ ... ]
- Обращается к данным для указанного поля в элементе. В этом примере это будет столбец price.
Number( ... )
- Приводит значение к Number, потому что функция format() требует параметр Number.
cfmt.format( ... )
- Форматирует значение как валюту.
Выберите то, что для Вас удобно, когда используете itemRenderer'ы. Некоторые люди только работают в ActionScript, который является хорошим, когда у Вас есть опыт с Flex и ActionScript. MXML также делает быстро работу простых itemRenderer'ов.