Рубрика: Маленькие хитрости
Определение версии AIR runtime
Иногда надо узнать, какая версия AIR runtime стоит на вашей машине. Это может понадобиться, например, чтобы написать баг-репорт.
Думаю, все умеют ее узнавать, но на всякий случай поделюсь своим способом, который работает на Windows XP (думаю, что на Висте не будет принципиальных отличий).
Для этого вам надо просто найти Adobe AIR в списке Add or Remove Programs (не знаю, как это по русски
)

После этого нажимаем на “Click here for support information” и видим номер версии:

Надеюсь, что кому-нибудь пригодится ![]()
Отлов событий из Item Renderer'ов
Многие знают, что ячейки таких компонент, как List и DataGrid могут быть представлены не только в стандартном виде, но и в произвольном, определённом пользователем. Достигается это с помощью item renderer’ов. Но не освещение темы item renderer’ов как таковых меня интересует сейчас, а некоторые конкретные случаи работы с ними. Те же, кто хочет узнать про рендереры, могут это сделать самостоятельно.
Основная задача item renderer’а - представить произвольный визуальный способ отображения и редактирования данных. То есть если речь идет об изменении данных, то renderer это делает самостоятельно. Замечу об отличии item renderer’а от item editor’а с точки зрения редактирования данных: item editor предоставляет механизм некой сессии редактирования данных, обладающей сложным устройством и возможностью отмены редактирования. Но это тоже отступление от нашей генеральной мысли об орехах.
Я хочу рассказать о таком варианте использования рендереров, как генерация некоторого события, которое не обязательно влечёт за собой изменение данных, ассоциированных с конкретным рендерером. Простейший пример - некоторая кнопка, о нажатии на которую мы хотим знать.
Как перехватить это событие? Сложный вопрос. В общем, есть разные варианты. Например, используя ключевые слова parent или owner. Но реально это не очень спортивный способ. Ибо сильно завязан на структуру объектов.
Я предлагаю использовать способ, базирующийся на бабблинге событий в визуальных объектах, о котором я недавно писал.
Итак, будем основываться на примере с List’ом и item renderer’е с кнопкой. Будем использовать простейший XML в качестве data provider’а:
XML:
<mx:XMLList xmlns=""> | |
<testDataElement id="1" name="test 1" label="Delete" /> | |
<testDataElement id="2" name="test 2" label="Delete" /> | |
<testDataElement id="3" name="test 3" label="Delete" /> | |
<testDataElement id="4" name="test 4" label="Delete" /> | |
<testDataElement id="5" name="test 5" label="Delete" /> | |
<testDataElement id="6" name="test 6" label="Delete" /> | |
</mx:XMLList> |
Тогда наш простой рендерер будет выглядеть так:
XML:
<?xml version="1.0" encoding="utf-8"?> | |
<mx:HBox | |
xmlns:mx="http://www.adobe.com/2006/mxml"> | |
<mx:Label | |
width="100%" | |
text="{data.@name}" /> | |
<mx:Button | |
click="dispatchEvent(new Event ('insideRendererButtonClick', true))" | |
label="{data.@label}" /> | |
</mx:HBox> |
При нажатии на кнопку мы генерим событие с бабблингом. Но как это использовать?
Есть два основных способа:
- Простой способ, не требующий модификации List’а.
- Более комфортный для использования, но требующий модификации List’а.
Первый способ прост. Мы ловим бабблинг снаружи обрабатываем его:
Можно посмотреть в отдельном окне (исходники доступны по правой кнопке).
Пример прост: при нажатии на кнопку в первом List’е удаляется соответствующая строка. Во втором же List’е меняется метка на кнопке (и это, теоретически, вполне можно сделать изнутри рендерера).
Как мы видим, главный класс приложения прост. Но он требует от нас двух вещей: подписываться на события с помощью addEventListener (декларативный синтаксис не работает потому что мы не указали соответствующих метаданных события в List’е) и на всякий случай вызывать stopPropagation для нашего события (чтобы где-то наверху кто-то неожиданно его не поймал). Также мы не можем послать стандартный ListEvent так как заполнение его полей требует знания о List’е, что опять же требует owner’ов/parent’ов.
Кстати, способ с бабблингом требует одного условия: имя события (тип) не должно совпадать ни с одним из имен событий нашего List’а.
Приложение, реализующее второй способ, выглядит аналогично. Также по правой кнопке доступны исходники. Рендерер мы оставили без изменений. Мы лишь добавили простой кастомный класс события и соответствующую константу для нашего события в нем. Это событие отражено в метаданных нашего расширенного List’а. Оно не перекрывается с именем события, которое «пузырит» наш рендерер.
Также в нашем List’е мы видим отключение дальнейшего бабблинга события от рендерера (чтобы оно никому уже не помешало) с использованием stopImmediatePropagation.

А также можно увидеть формирование валидного события. Все просто. И, как следствие, упростился код главного класса приложения и стал много нагляднее.
Когда использовать эти способы? Первый способ хорошо подходит для прототипирования. Он быстрый и простой. Второй же способ лучше использовать при создании коммерческого приложения, особенно если речь идет о командной разработке.
Примерно так. Можно задавать вопросы, делиться своими способами, критикой приведенных итд. Милости прошу в комменты.
Ну и напоследок предлагаю скачать готовый проект приведённого примера (Flex Builder 3 beta 3).
Еще о событиях в ActionScript

Если кто-то помнит, на первой встрече Русскоязычной Группы Пользователей Flash-платформы (RAFPUG) я рассказывал о событиях в ActionScript 3. Как краевед могу сказать, что тема событий в ActionScript далеко не так тривиальна, как это может показаться на первый, второй и третий взгляд. Работая с ними довольно долго, можно тем не менее иногда обнаружить что-то новое, сделать новые выводы и найти применения старым вещам. В частности, в том докладе я сказал некоторую лажу и во всем зале меня никто не поправил
Поправлю себя сам.
Там я сказал, что event flow (поток событий) работает только для визуальных объектов и только для интерактивных событий мыши и клавиатуры, генерируемых Flash-плеером. На основании этого я продемонстрировал пример с искусственным бабблингом (фазой пузырька). И сделал слегка неверные выводы.
На самом деле, на практике все гораздо проще. И мой тот пример вполне работоспособный, но более актуальный для невизуальных объектов, либо для того, чтобы объекты генерировали целевую фазу (target phase) и фазу пузырька (bubbling phase) одновременно. Речь идет о делегировании события.
Для простой же фазы пузырька в визуальных объектах практически ничего не надо, и сейчас я это проиллюстрирую.
В изначальном утверждении про визуальные объекты и поток событий все правильно если рассматривать под потоком событий все три фазы (capture, target и bubbling). Но фаза пузырька (bubbling) работает и так, сама по себе.
Рассмотрим пример:
Пример сделан в виде просто AS-проекта для наглядности. Исходники можно посмотреть тут. Скачать проект можно тут.
Что мы видим на данном примере. Есть корневой Sprite, в котором лежит простой компонент ParentSprite (внешний серый квадрат), в нем лежит компонент ChildSprite, при клике на внутренний квадрат которого наш компонент генерит событие, которое ловится внешним Sprite‘ом безо всякого участия ParentSprite:
- private function onInsideChildClick (event:MouseEvent):void
- {
- dispatchEvent(new Event ("ourTargetEvent", true));
- }
Обработка события видна в текстовом поле. Весь секрет в волшебных пузырьках во втором параметре конструктора класса Event (он установлен в true). Это и есть бабблинг. И ParentSprite автоматом бабблит его. Безо всякого кода.
Естественно, это просто иллюстрация. В реальности неплохо бы сделать кастомный класс события, соответствующие константы итд.
Гарантирую, что это может сильно облегчить вам жизнь.
Мои любимые плагины для Eclipse
Многие пользуются Eclipse в каждодневной разработке. И все знают, что сила Eclipse в плагинах. В этом посте я хочу описать несколько своих любимых плагинов. Возможно, они вам пригодятся. Сразу замечу, что все обозреваемые плагины бесплатны и свободно доступны. Итак:
Winamp plugin. Плагин добавляет в Eclipse панельку с кнопками и соответствующее меню:

Смысл понятен. При запущенном Winamp’е можно управлять проигрыванием песен. Для работы плагина необходимо скопировать
{Eclipse installation}/plugins\org.jeneag.winamp_1.0.2\cpp\winamp.dllв папку с dll-ками вашей jre.Мне данный плагин нравится

AnyEdit tools plugin for Eclipse. Об этом плагине я уже подробно писал ранее.
Eclipse Colorer. Плагин позволяет просто и быстро редактировать файлы множества разных форматов, осуществляет в них подсветку кода, поддерживает folding, и word wrapping. Также очень интересной особенностью является возможность генерации HTML-файла с раскрашенным в выбранной цветовой схеме кодом. Поддерживает более 150 типов файлов, среди которых js, java, xml, as, php, html, Ruby (в разделе Rare languages) и многое другое

Subclipse. Всем известный плагин для работы с Subversion.
- EditorPosition. Запоминает положение курсора в редактируемом файле. Если вы закрываете Eclipse, то при его последующем открытии восстанавливаются не только открытые закладки-файлы, но и место, где вы закончили их редактировать.
Здесь я перечислил лишь основной набор, который подходит всем разработчикам, работающим в Eclipse. То есть плагины не привязаны к конкретному языку программирования.
Надеюсь, что и у вас есть любимые полезные плагины, ссылки на которые вы укажете в комментариях.
И напоследок полезный совет, которым многим известен, а некоторым - нет. Если в окне навигатора щелкнуть на проекте или папочке правой кнопкой и выбрать Go Into, то данная папочка станет корневой в данном навигаторе. Это позволит вам убрать на время ненужный мусор и сконцентрироваться на главном. Вернуться назад просто: достаточно нажать стрелку “назад” в меню навигатора.
Удачи! ![]()
Аспекты наследования во Flex
Если кто-то занимался исследованием кода компонент Flex-фрэймворка, мог заметить некоторые особенности реализации метода createChildren() практически во всех классах:
- override protected function createChildren():void
- {
- super.createChildren();
- if (!titleBar)
- {
- titleBar = new UIComponent();
- ...
- rawChildren.addChild(titleBar);
- }
- if (!titleTextField)
- {
- titleTextField = new UITextField();
- ...
- titleBar.addChild(titleTextField);
- }
- ...
- }
Примерно в таком духе.
Не очень понятно, зачем проверять существование создаваемых GUI-элементов, если метод createChildren() вызывается из конструктора, и именно в нем создаются контролы? Думаю, что многие знают, для остальных смысл объясняется ниже.
Предположим мы хотим создать вариант класса mx.controls.Alert, который может отображать текст с HTML-форматированием. Задача может показаться сложной, но описанное выше дизайнерское решение создателей Flex-фрэймворка существенно облегчает нам задачу, делая ее очень легкой.
Рассмотрим место в коде mx.controls.Alert, которое нас интересует:
- override protected function createChildren():void
- {
- super.createChildren();
- ...
- if (!alertForm)
- {
- alertForm = new AlertForm();
- alertForm.styleName = this;
- addChild(alertForm);
- }
- }
Именно поле alertForm содержит экземпляр класса mx.controls.alertClasses.AlertForm, в котором и находится наше текстовое поле. Идея простая: нам надо создать свой класс AlertForm и попробовать создать его экземпляр в методе createChildren() класса com.riapriority.controls.HTMLAlert, который мы хотим получить.
Логично предположить, что наш класс com.riapriority.controls.HTMLAlertForm будет наследоваться от оригинального AlertForm. И тогда благодаря проверке на существование alertForm мы преспокойно можем написать наш метод createChildren():
- override protected function createChildren():void
- {
- alertForm = new HTMLAlertForm();
- alertForm.styleName = this;
- addChild(alertForm);
- super.createChildren();
- }
Итак, идея понятна. Если же мы хотим дать возможность и другим проделывать ту же операцию, то лучше написать так:
- override protected function createChildren():void
- {
- if (!alertForm)
- {
- alertForm = new HTMLAlertForm();
- alertForm.styleName = this;
- addChild(alertForm);
- }
- super.createChildren();
- }
То же самое проделываем в классе com.riapriority.controls.HTMLAlertForm:
- override protected function createChildren():void
- {
- if (!textField)
- {
- textField = new UITextField();
- textField.styleName = this;
- textField.htmlText = Alert(parent).text;
- textField.multiline = true;
- textField.wordWrap = true;
- textField.selectable = true;
- addChild(textField);
- }
- super.createChildren();
- }
Единственная проблема в том, что статический метод show() класса mx.controls.Alert придется продублировать в нашем HTMLAlert: статические методы не наследуются. Итоговый класс com.riapriority.controls.HTMLAlert:
- package com.riapriority.controls
- {
- import flash.display.Sprite;
- import mx.controls.Alert;
- import mx.core.Application;
- import mx.core.mx_internal;
- import mx.events.CloseEvent;
- import mx.managers.PopUpManager;
- use namespace mx_internal;
- public class HTMLAlert extends Alert
- {
- public function HTMLAlert()
- {
- super();
- }
- override protected function createChildren():void
- {
- if (!alertForm)
- {
- alertForm = new HTMLAlertForm();
- alertForm.styleName = this;
- addChild(alertForm);
- }
- super.createChildren();
- }
- public static function show(text:String = "",
- title:String = "",
- flags:uint = 0x4 /* Alert.OK */,
- parent:Sprite = null,
- closeHandler:Function = null,
- iconClass:Class = null,
- defaultButtonFlag:uint = 0x4 /* Alert.OK */):Alert
- {
- var modal:Boolean = (flags & Alert.NONMODAL) ? false : true;
- if (!parent)
- parent = Sprite(Application.application);
- var alert:Alert = new HTMLAlert();
- if (flags & Alert.OK||
- flags & Alert.CANCEL ||
- flags & Alert.YES ||
- flags & Alert.NO)
- {
- alert.buttonFlags = flags;
- }
- if (defaultButtonFlag == Alert.OK ||
- defaultButtonFlag == Alert.CANCEL ||
- defaultButtonFlag == Alert.YES ||
- defaultButtonFlag == Alert.NO)
- {
- alert.defaultButtonFlag = defaultButtonFlag;
- }
- alert.text = text;
- alert.title = title;
- alert.iconClass = iconClass;
- if (closeHandler != null)
- alert.addEventListener(CloseEvent.CLOSE, closeHandler);
- PopUpManager.addPopUp(alert, parent, modal);
- alert.setActualSize(alert.getExplicitOrMeasuredWidth(),
- alert.getExplicitOrMeasuredHeight());
- return alert;
- }
- }
- }
Итоговый класс com.riapriority.controls.HTMLAlertForm:
- package com.riapriority.controls
- {
- import mx.controls.Alert;
- import mx.controls.alertClasses.AlertForm;
- import mx.core.UITextField;
- import mx.core.mx_internal;
- use namespace mx_internal;
- public class HTMLAlertForm extends AlertForm
- {
- override protected function createChildren():void
- {
- if (!textField)
- {
- textField = new UITextField();
- textField.styleName = this;
- textField.htmlText = Alert(parent).text;
- textField.multiline = true;
- textField.wordWrap = true;
- textField.selectable = true;
- addChild(textField);
- }
- super.createChildren();
- }
- }
- }
Использование стандартное - HTMLAlert.show (...). Но в тексте теперь можно задавать HTML-форматирование.
Не так много кода, не правда ли?