Показаны сообщения с ярлыком MXML. Показать все сообщения
Показаны сообщения с ярлыком MXML. Показать все сообщения

воскресенье, марта 22, 2009

mx:Pause vs flash.utils.Timer. Flex

Декларативность MXML - огромное достоинство Flex.
Легкое определение переменных в MXML значительно упрощает разработку:
  • позволяет легко и наглядно определять их свойства и обработчики событий
  • автоматически делает переменные Bindable
  • позволяет их связать с другими переменными и выражениями
  • делает наглядным код

Но, на сколько я понял, не все классы можно представить в декларативном виде.

Например, класс flash.utils.Timer невозможно представить в виде MXML. Компилятор выдает ошибку "Неправильное количество аргументов". Всё от того, что конструктор этого класса содержит один обязательный параметр. Как его указать, и возможно ли это, увы, я не понял.

Но есть простое решение - использовать другой класс, хорошо подходящий по своему функционалу - mx.effects.Pause:

<mx:Pause
id="somePause"
duration="2000"
effectEnd="somePause_effectEndHandler(event)"
/>

. . .

//Стартовать задержку
this.somePause.play();

. . .

private function somePause_effectEndHandler(event:EffectEvent):void {
//Обработать окончание задержки
}

четверг, марта 19, 2009

ASDoc in MXML. Правильно комментируем MXML.

MXML для Flex-разработки имеет первостепенное значение. Однако, воспринимая его как порождение XML, до некоторых пор, я не считал его языком программирования или чем-то в этом духе.
Теперь, уже достаточно сроднившись с MXML, понимаю, что есть необходимость правильно его комментировать. Просматривая листинги MXML, глаз так же нуждается в разделении блоков, как и при чтении AS-кода.
Если с AS-кодом всё давно ясно (я его использую везде где надо и где необязательно), то вот с MXML хотелось бы разобраться. И поможет нам в этом статейка "ASDoc in MXML - Functional and Design Specification". Выделим основные мысли из нее.

Основные положения

В настоящее время не существует способа документировать MXML-компоненты. (Кстати, в лайфдоках про это вот что сказано: Documenting MXML files). Компилятор asdoc не обрабатывает комментарии в MXML-файлах. Но число компонентов, разрабатываемых на базе MXML неуклонно растет, поэтому поддержка в asdoc файлов MXML очень бы помогла разработчикам при создании документации.

ASDoc комментарий

Для того, чтобы ASDoc-комментарий был обработан asdoc-компилятором, необходимо указать 3 тире после <!:
<!--- asdoc comment -->
Для сравнения - обычный комментарий начинается с 2-х тире.

Комментирование MXML-компонентов и компонента уровня класса

Комментарии для компонентов внутри MXML должны располагаться перед компонентами.
Комментарий компонента уровня класса должен располагаться перед корневым тегом MXML:

<?xml version="1.0"?>

<!-- Standard MXML comment: events\myComponents\MyButton.mxml -->

<!---
The class level comment for the component.
This tag supports all ASDoc tags, and does not require a CDATA block.

@see mx.container.VBox
-->
<mx:VBox xmlns="http://ns.adobe.com/mxml/2009" xmlns:mx="library:adobe/flex/halo" >

<!--- Comment for button -->
<mx:Button id="myButton" label="This button has comment"/>

<!--- This comment doesn't belong to any component and will be ignored -->

</mx:VBox>

Комментарии перед тегами Script, Metadata будут игнорироваться. Перед тегами Definition, Library, Private и внутри их, комментарии так же будут игнорироваться.

Итог

В статье приводятся примеры использования ASDoc комментирования в различных его применениях. Рассматривать их подробно я не буду - всё достаточно прозрачно.

Вывод таков - MXML комментировать надо. И для этого уже существует прототип (если я правильно понял) стандарта, которого и следует придерживаться.

четверг, марта 05, 2009

Дружим Flex с Flash. Заметки: загрузка, внедрение, шрифт. Flex

Закончился очередной дидлайн. После крупных проектов, которые делаются за 3-4 дня практически с нуля, остаются ощущения, схожие с состоянием после автобусного тура: в голове каша, мельком что-то увидел, урывками что-то запомнил, надо разобрать фотки. Теперь настало время немного отдышаться и закрепить полученный опыт.

Для начала, несколько заметок:

  • Перенос строки в строках MXML: символ &#xd; - мелочь, а приятно.
  • Загрузка сложных Flash-приложений в SWFLoader должна производиться в выделенный домен приложения, т.к. могут быть конфликты классов:
<mx:SWFLoader autoLoad="false" id="swfLoader" source="application.swf" />

. . .

private function creationCompleteHandler(event:Event):void {
swfLoader.loaderContext=new LoaderContext(false,new ApplicationDomain());
swfLoader.load();
}
  • Если внешнее приложение подгружается в SWFLoader, есть вероятность, что в какой-то момент SWFLoader потеряет stage, что может пагубно отразиться на функциональности этого приложения. Если приложение где-то внутри обращается к stage и не обрабатывает его недоступность, то будут генерироваться ошибки. Побороть этот недостаток я не смог - только устранив все "не защищенные" stage из внешнего приложения.
  • Внедрение шрифтов. Гораздо надежнее внедрять шрифт непосредственно из TTF файла. Внедрение из файла SWF, в котором, в свою очередь, внедрен необходимый шрифт лучше оставить для тяжелых случаев несовместимостей с Flex. Дело в том, что почему-то, внедренные в SWF разные стили (normal, bold) шрифта не корректно "извлекаются" Flex-ом. Он не дает назвать разные стили разными именами (как это можно сделать в первом случае), теряет одно из начертаний (жирный шрифт просто не выводится). Возможно, это из-за особенностей конкретных шрифтов, но первый метод срабатывает гораздо стабильнее.
@font-face
{
fontFamily: "Century Schoolbook";
fontWeight: normal;
fontStyle: normal;
src: url("file://./assets/fonts/CENSCBK.TTF");
unicodeRange:
U+0020-U+0040, /* Punctuation, Numbers */
U+0041-U+005A, /* Upper-Case A-Z */
U+005B-U+0060, /* Punctuation and Symbols */
U+0061-U+007A, /* Lower-Case a-z */
U+007B-U+007E, /* Punctuation and Symbols */
U+00FC-U+00FD,
U+0410-U+042F, /* Cyrillic Upper-Case A-Z */
U+0430-U+0451; /* Cyrillic Lower-Case a-z */
}

@font-face
{
fontFamily: "Century Schoolbook Bold";
fontWeight: bold;
fontStyle: normal;
src: url("file://./assets/fonts/SCHLBKB.TTF");
unicodeRange:
U+0020-U+0040, /* Punctuation, Numbers */
U+0041-U+005A, /* Upper-Case A-Z */
U+005B-U+0060, /* Punctuation and Symbols */
U+0061-U+007A, /* Lower-Case a-z */
U+007B-U+007E, /* Punctuation and Symbols */
U+00FC-U+00FD,
U+0410-U+042F, /* Cyrillic Upper-Case A-Z */
U+0430-U+0451; /* Cyrillic Lower-Case a-z */
}


  • Внедрять клип из внешней SWF так, чтобы работали его фреймовые скрипты можно следующим образом:
    source="@Embed(source='assets/Index.swf')"
    При внедрении отдельного символа, его фреймовые скрипты и скрипты всех вложенных объектов теряются:
    source="@Embed(source='assets/Index.swf'#Index)"
    Игра с параметром mimeType не помогает.

На управлении внедренным контентом я бы хотел остановиться подробнее. Если у нас много однотипного материала для внедрения вышеописанным способом, то плодить SWF-файлы не удобно, плюс, внедряя каждый файл, мы не можем обобщать графику - она будет внедряться для каждого файла заново (не проверял, но догадываюсь).

Куда удобнее создать одну большую библиотеку и из нее внедрять символы. Но, к сожалению, их скрипты уже будут не доступны.
Но у нас есть в распоряжении метки фреймов и имена клипов. Совмещая возможности
MovieClip.currentLabels и MovieClip.addFrameScript можно получить контроль над внедренным клипом и заставлять его в нужный момент выполнять необходимые нам действия: останавливать клип на нужном фрейме, генерировать события и даже передавать данные в текстовые поля клипа.

Создаем компонент - наследник SWFLoader. После инстанцирования класса внедренного клипа (конец метода load(), можно так же добавить и в обработчик события загрузки), считываются все его метки и в их фреймы добавляются необходимые скрипты. Имя каждой метки имеет ключевую составляющую, которая и определяет тип действия добавляемого скрипта.
В нужный момент можно заставить клип остановиться, перейти на другую метку, генерировать событие (с идентификатором, так же зашитым в имя метки), просканировать клип и получить срез всех визуальных объектов, чтобы делать с ними всё что угодно. Главное - этот момент устанавливается прямо в таймлайне клипа, а не путем каких-либо расчетов количества фреймов или подобных методов.


Прекрасный момент - праздник еще не настал, а дидлайны уже позади! Отдыхать и набираться сил.

И конечно, наших прекрасных женщин поздравляю с праздником 8 марта!

среда, февраля 06, 2008

Событие от компонентов itemRenderer. Flex

Есть компонент список List, элементы которого являются компонентами (MXML) и задаются через свойство itemRenderer.
В свойство dataProvider указан [Bindable] массив ArrayCollection.
Каждый из элементов этого массива - объект [Bindable] класса с набором свойств, который порождается от EventDispatcher. В компонентах элементов списка свойства этого объекта указаны как источники данных: text="{data.name}" и change="data.text=text_input.text;".

При работе со списком, происходят изменения свойств любого из объектов списка. Свойства объектов элементов массива обновляются, меняют свои значения. Все хорошо.

Теперь, требуется получать событие обновления какого-либо элемента.
Я пошел обычным путем - подписался на прослушивание события PropertyChangeEvent.PROPERTY_CHANGE для каждого экземпляра элемента массива при его создании. Приблизительный код:

. . .

[Bindable]
public var some_list:ArrayCollection;

. . .

var item:TSomeListItem=new TSomeListItem(some_source.name, some_source.status);
item.addEventListener PropertyChangeEvent.PROPERTY_CHANGE,this.onPropertyChange,false,0,true);
this.some_list.addItem(item);

. . .

private function onPropertyChange(event:PropertyChangeEvent):void {
trace("Изменилось свойство", event.source, ".", event.property,"Было:", event.oldValue,"Стало:",event.newValue, "Тип изменения:", event.kind);
}

. . .

<mx:HorizontalList . . . dataProvider="{some_list}" itemRenderer="SomeListItemRenderer" id="some_list_box"></mx:HorizontalList>

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

вторник, февраля 05, 2008

Параметры конструктора. MXML

Определяя класс для последующей его вставки в MXML, при объявлении конструктора, необходимо указать для всех его параметров значения по умолчанию. Иначе возникает ошибка "1136: Incorrect number of arguments. ".
Других способов избежать ошибки я не нашел.

понедельник, февраля 04, 2008

ViewStack и эффекты появления/исчезания. MXML

Компонент ViewStack удобен для строительства последовательностей диалогов. Чтобы диалоги появлялись/исчезали интереснее, можно использовать эффекты (способ применим к любым компонентам, это частный случай).
Определяем эффекты:
<mx:Move id="fadeIn" duration="500" xFrom="-1000"/>
<mx:Move id="fadeOut" duration="500" xTo="-1000"/>

Для каждого компонента указать эффекты появления/исчезания:
<mx:ViewStack . . . selectedIndex="0">
<mx:Canvas . . . hideEffect="{fadeOut}" showEffect="{fadeIn}">

четверг, января 31, 2008

Видео во Flash/Flex. Проблемы позиционирования.

Задачу быстро сделать проигрыватель видео (управление видео-презентацией) я оценил в пару часов. Т.к. совсем недавно разобрался с воспроизведением высококачественного видео, с тех-частью проблем не должно было возникнуть. Проект начал делать на Flex, дабы лишний раз попрактиковаться.

Воспроизводить видео действительно получилось быстро. Класс VideoDisplay, управляемый несколькими кнопками и слайдером.
И тут встали более сложные задачи.
  1. Необходимо в определенные моменты времени останавливать видео (презентация).
  2. Небходима навигация по этим стоп-точкам.

Первая задача создала одну основную проблему: Получить видео-файл, содержащий сигнальные-точки (cue points).
Видео мы выгоняли из AfterEffects в MOV кодеком H.264. Можно ли туда загнать сигнальные-точки (которые, собственно были расставлены в тайм-лайне) и как это сделать - не смогли выяснить.
Решили экспортировать в FLV (задрав качество до порядка 3000) - расставить сигнальные точки пришлось вручную в "Adobe Flash CS3 Video Encoder". В принципе, была идея выгнать видео в MOV (высокое качество) и FLV (минимального качества), использовать сигнальные точки FLV а воспроизводить MOV.
Для получения сигнальных точек использую следующий код:
private function onMeta(event:MetadataEvent):void {
this.slider.maximum=event.info.duration;
this.__cue_points=event.info.cuePoints;
}
. . .
<mx:VideoDisplay metadataReceived="this.onMeta(event)" . . . >
. . .

Таким образом, по событию metadataReceived - когда Flex получает метаданные видео-файла - мы получаем массив сигнальных точек.
Внимание - свойство cuePoints класса VideoDisplay не будет содержать сигнальные точки, которые находятся в метаданных видео-файла. Это свойство нужно для "ручной" установки сигнальных точек. Управление (добавление/удаление) сигнальными точками cuePoints осуществляется менеджером сигнальных точек cuePointManager. Можно задать их прямо во Flex:
<mx:VideoDisplay . . . >
<mx:cuePointManagerClass>mx.controls.videoClasses.CuePointManager</mx:cuePointManagerClass>
<mx:cuePoints>
<mx:Array>
<mx:Object time="0" name="point1"/>
<mx:Object time="3.034" name="point2"/>
<mx:Object time="4.774" name="point3"/>
<mx:Object time="8.344" name="point4"/>
<mx:Object time="12.271" name="point5"/>
</mx:Array>
</mx:cuePoints>
</mx:VideoDisplay>

Итак, мы можем контролировать сигнальные точки. Переходим к обработке пауз на этих точках. Событие cuePoint отлично справляется с нашей задачей:
private function onCue(event:CuePointEvent):void {
this.video_display.pause();
}
<mx:VideoDisplay id="video_display" cuePoint="this.onCue(event)" . . . >

При обработке события сигнальной точки, воспроизведение видео останавливается четко в том месте, которое задает сигнальная точка. Это превосходно.

Задача номер два. А теперь начинается самое нехорошее. Мы имеем массив точек (каждый элемент содержит объект с двумя свойствами: name - имя маркера, time - время в секундах), и можем позиционировать воспроизведение видео по его элементам. Казалось бы, чего проще:
private function onClickNext(event:FlexEvent):void {
this.video_display.playheadTime=cue_points[position].time;
}

Ан нет. Видео позиционируется не точно в указанную позицию - то перескочет на пару секунд, то недотянет. Причем точность настолько низка, что нам пришлось отказаться от такого способа позиционирования.
По этому поводу, в документации Flex к свойству playheadTime сказано, что для FLV-файла позиционирование происходит в ключевой фрейм, ближайший к указанной позиции, причем эти ключевые фреймы формируются во время кодирования видео. Поэтому, извиняйте, но точно спозиционироваться в указанное место не получится, если там по близости нет ключевого фрейма. Попробовал позиционировать по тем же контрольным точкам MOV файл. Та же петрушка.
Эксперименты показали, что если видео экспортировать в SWF и позиционироваться по фреймам тайм-лайна, точность позиционирования абсолютна.

Раз нельзя реализовать нормальную навигацию по подгружаемому MOV/FLV видео, то дальнейшая разработка этого проекта на Flex потеряла всякий смысл.
Проще было импортировать видео в SWF, загнать его в мой старый, проверенный презентационный AS2 движок и обработать массив фреймов (составленный вручную за 15 минут), что и было сделано примерно за час.

Отрицательный результат - тоже результат. Потратив день на разборки, я узнал мгного нового и получил хороший опыт Flex-разработки.

вторник, января 29, 2008

Тег [Bindable]. Flex

Если нам необходимо связать два свойства так, чтобы при изменении первого (источника) Flex автоматически копировал данные во второе (приемник), мы можем использовать метатег [Bindable]. Указав [Bindable] перед свойством-источником, мы даем понять Flex, что при изменении свойства надо генерировать событие.
[Bindable]
[Bindable(event="eventname")]

Если событие не указано явно, то по умолчанию создается событие propertyChange.

Тег [Bindable] используется в трех случаях:

  1. Перед определением класса. В этом случае, для всех (только!) public свойств (и переменных, и установщиков) Flex автоматически генерирует событие propertyChange типа PropertyChangeEvent, и все эти свойства могут использоваться как источники связывания данных.
    [Bindable]
    public class TextAreaFontControl extends TextArea {}
  2. Перед public, protected, private свойствами, определенными как переменные.
    При изменении свойств, Flex автоматически генерирует событие propertyChange типа PropertyChangeEvent. Можно также указать другое имя события.
    [Bindable(event="fooChanged")]
    public var foo:String;
  3. Перед public, protected, private свойствами, определенными как установщики. Для того, чтобы свойство являлось источником связывания данных, необходимо чтобы были определены и set, и get методы.
    Если указан только set метод, создается свойство "только для записи" и его нельзя использовать в качестве источника связывания данных.
    Если же указан только get метод, создается свойство "только для чтения", которое можно использовать как источник связывания данных и без вставки метатега - аналогично переменным, определеных как const.
    При изменении свойств, Flex автоматически генерирует событие propertyChange типа PropertyChangeEvent. Можно также указать другое имя события.
    [Bindable]
    public function set shortNames(val:Boolean):void { ... }
    public function get shortNames():Boolean { ... }

ВАЖНО: Если после изменения, свойство не приобрело новое значение, Flex не генерирует событие. Проверка осуществляется по принципу (oldValue !== value).

ВАЖНО: Если свойство содержит ссылку на объект и эта ссылка заменяется на другую, то событие генерируется. Если же изменяется объект, на который ссылается свойство, событие генерироваться не будет.

Свойства read-only и static по-умолчанию являются источниками связывания данных.

В этом примере установщик обновляет значение свойства, и вызывает событие для обновления связываемых свойств.

// Define private variable.
private var _maxFontSize:Number = 15;

[Bindable(event="maxFontSizeChanged")]
// Define public getter method.
public function get maxFontSize():Number {
return _maxFontSize;
}

// Define public setter method.
public function set maxFontSize(value:Number):void {
if (value <= 30) {
_maxFontSize = value;
} else _maxFontSize = 30;
// Create event object.
var eventObj:Event = new Event("maxFontSizeChanged");
dispatchEvent(eventObj);
}

Работа с цепочками связываемых свойств.

Следующий пример показывает довольно длинную цепочку связываемых свойств:
<mx:Text id="myText" text="{user.name.firstName.text}"/>

Для того, чтобы механизм связывания данных поймал изменения свойства text, достаточно сделать свойство text связываемым. Однако, если необходимо присвоить новое значение какому-либо элементу цепочки на этапе выполнения, то все они должны быть связываемыми. С другой стороны, изменение любого из элементов приведет к неспособности механизма связывания отслеживать изменение свойства text.
В случае работы с такими цепочками, можно пользоваться методами BindingUtils.bindProperty() или BindingUtils.bindSetter() .
Пример использования bindProperty():
bindProperty(myText, 'text', user, ["name","firstName","text"]);

Здесь представлена цепочка в виде ["name","firstName","text"] относительно объекта user. При этом user не является элементом цепочки.
В MXML цепочка связываемых свойств всегда задается относительно this:
bindProperty(myText, 'text', this, ["user", "name","firstName","text"]);

понедельник, января 28, 2008

Фокус на кастомном Flex-компоненте

Внедряется кастомный компонент, в котором задействована клавиатура.
После того, как последовательность интерфейсов доходит до экрана с этим компонентом, он активируется. Но не принимает события от клавиатуры. Необходимо установить на него фокус:
Либо добавить в параметры MXML-тега:
creationComplete="setFocus();"

Либо инициализировать в скрипте:
this.[component_name.]setFocus();