В первой части я показывал Вам, как сделать действующий 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'ов.