| « Список рунетовских Flex-приложений | Еще о событиях в ActionScript » |
Отлов событий из 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).
Трекбек адрес этой записи
URL трекбека (щелкните правой кнопкой мыши и скопируйте ссылку)
14 комментариев
я, честно говоря, к своему стыду довольно поверхностно разбираюсь в модели событий. так, где-то поймать, кого-то подписать. а что там внутри, разные фазы, от кого оно идет, где можно и нужно остановить не знаю. предыдущую статью добавил в закладки. прочитаю на выходных (8
Кстати, есть мысль подобные технические посты дублировать к Росту в вики, чтобы не пропадало с течением времени. Что думаешь?
Ну вот и ты стал писать кодерские статьи
Ну когда у меня есть на это время, я всегда стараюсь писать. Но оно есть нечасто
Кстати, есть мысль подобные технические посты дублировать к Росту в вики, чтобы не пропадало с течением времени. Что думаешь?
Ну я не против если кто-нибудь это будет делать. Желательно со ссылкой на оригинал
Не обязательно ждать, пока событие начнет пузыриться. Можно споймать его на фаже "захвата" (сapure phase).
Для этого в методе addEventListener вcего и надо, что третьим параметром (useCapture) передать true.
Подозреваю, что на приведенной схеме есть неточность. Событие попадет таки в itemEditor, выйдет из него (фаза пузырения) и попадет в обработчик. Предложенный мной вариант отловит его на "подходе" к рендереру.
Все отлично учавствует. Только что проверил. Кастом ивент диспатчится из рендерера и в рендере же на него вешается обработчик. Без useCapture сначала попадаем в обработчик в рендерере, а потом уже в обработчик листа.
С useCapture все начинается и заканчивется в обработчике листа (в обработчик рендерера не попадаем из за event.stopPropagation())
Спасибо за наводку!
Спасибо за наводку!
Be my guest!
Спасибо за хороший блог