четверг, Июнь 04, 2009
Ух ты! Binding
Вот к примеру, обычно мы делаем вот так:
<someComponent someAttribute="{someValue1+'_splitter_'+someValue2}"/>
А оказывается можно и так:
<someComponent someAttribute="{someValue1}_splitter_{someValue2}"/>
Выбираем движок для просмотра Flash-панорам
Для начала - немного теории панорам: Панорамная фотография, BASICS (здесь же можно найти и другую информацию про панорамное фото, софт, вьюверы).
По flash-вьюверам панорам, Гугл выдал следующих претендентов:
- Flash Panorama Player - платный, недорогой вьювер кубических панорам. Принцип прост - имя swf-файл вьювера должно соответствовать имени jpg-файлов, которые имеют соответствующие сторонам куба суффиксы.
- Ryubin's Flash Panorama Laboratory - Отличный движок, без исходников, но настраиваемый через XML.
- krpano - платный, недорогой вьювер с кучей дополнительных фич, типа эффект линз и тп.
- PanoSalado, Spincontrol - опенсорсный движок на базе PV3 и AIR-утилита для сборки виртуальных туров. Есть и исходники и документация.
среда, Июнь 03, 2009
Code-behind или mx:Script?
<mx:Script source="includes/ComponentName.as"/>Каких-либо логических недостатков такого метода я не наблюдал, однако столкнулся с постоянной глючностью автокомплита AS-редактора FlexBuilder. И вот, когда меня в конец это достало, решил взять на вооружение способ Code-behind.
Про Code-behind пишут следующее:
Code Behind
Building components by using code behind
Code-Behind in Flex 2
Code-behind gotcha in Flex Builder for AIR apps
Советы новичкам
В кратце, суть проста - наш MXML компонент ComponentName не базируется напрямую на основном компоненте, например, Canvas, а на компоненте-"прослойке" ComponentNameClass (также используется суффикс -Base). Код этого компонента размещается в ComponentNameClass.as. Его класс является потомком класса того самого основного компонента (Canvas) и содержит весь необходимый AS-код.
Все компоненты, которые имеются в ComponentName.mxml и к которым нужен доступ, должны быть объявлены в ComponentNameClass.as как public. Методы и обработчики событий - public или protected.
Вообще, конечно, это очень правильный подход. Только напрягает пара фактов - нужно возиться с объявлением класса и объявлять все компоненты, с которыми необходимо иметь дело в as-файле. Если, к примеру, я вдруг передумаю использовать в MXML вместо LinkButton (а он уже объявлен так в AS-компоненте) простой Button, получу ошибку - необходимо везде сделать замену. Выход - объявлять их дальних предков или вообще интерфейсы.
А вот интересно - есть ли средства автоматизации этого процесса? Что-то типа команды "Create Code-behind Class" или при создании MXML-компонента подобная галочка. Я не нашел...
суббота, Май 16, 2009
Модули, синглтоны, Type Coercion Failed и спасение
Сегодня столкнулся с такой проблемой:
Приложение использует модули. Один из модулей содержит ComboBox.
Во время работы, подгружается то один модуль, то другой. Так вот.
При первой активации, модуль с ComboBox работает корректно. Однако, при повторной активации модуля, начинаются проблемы - при нажатии на ComboBox вылетает ошибка:
TypeError: Error #1034: Ошибка типа Coercion: невозможно преобразовать mx.managers::PopUpManagerImpl@6c0ce41 в mx.managers.IPopUpManager.
Аналогичная ошибка с компонентом List, правда немного с другими классами:
TypeError: Error #1034: Ошибка типа Coercion: невозможно преобразовать mx.managers::DragManagerImpl@6b7ec11 в mx.managers.IDragManager.
Ужасаясь перспективе просидеть в дебаге всю ночь, полез в Гугл. И тут же нашел спасение - не один я столкнулся с таким казусом:
Flex Error #1034: Type Coercion Failed: Cannot Convert Mx.managers
Вот решение (я его оформил немного поизящнее):
Нужно в коде основного приложения указать следующую строку:import mx.managers.*;DragManager;HistoryManager;PopUpManager;
Одним индусам известно, что случается с этими синглтонами после выгрузки модуля, могу предположить, что таким образом, мы принуждаем компилятор внедрять эти классы в основное приложение а не в модули. В ином случае, они внедряются в модуль, и, скорее всего, в какой-то момент теряются.
воскресенье, Апрель 05, 2009
Записываем звук с помощью Red5
Как обычно бывает, с момента зарождения проекта, уходят месяцы на процесс впаривания, продажи, рисования и утверждения дизайна. И вот, наступает очередь разработчиков, и, как обычно, на flash выделяется неделя, в лучшем случае - две (какая несправедливость).
В этот раз, правда, всё было несколько иначе - разработка началась как раз примерно за неделю до моего отлета в Египет. Поэтому, пришлось сделать максимум, чтобы без меня две недели люди всё это ставили на свои рельсы. Принцип вынесения всех настроек в FlashVars решает эту проблему на раз. Главное не забыть составить подробный мануал.
Итак, мы пришли к решению, что лучше в качестве медиа-сервера использовать Red5. Процесс установки под Windows и тестирования Red5 для локальной разработки я описал здесь: Установка Red5 и всё что нужно для Red5-разработки. По сути, в этом посте изложено всё необходимое для создания минимального приложения для записи звука/видео. В качестве формата хранения звуковых данных, решили оставить FLV. Сначала планировали конвертацию FLV в MP3 на сервере, и даже были идеи как это реализовать. Но позже решили, что для нужд проекта это излишество и лишняя нагрузка на сервер. Все записи сохраняются как есть, в формате FLV, и воспроизводятся проигрывателем, который, кстати, по виду и функционалу совсем не отличается от того, что воспроизводит MP3.
С flash-частью оказалось всё слишком просто. Достаточно было использовать версию Flash 8 и AS2. За основу я взял пример из ...\Red5\swf\samples\SimpleRecorder.fla. С момента окончания разработки прошло уже около месяц и многое забылось, но вот пара важных заметок:
- Для записи, уровень тишины необходимо ставить в 0 (setSilenceLevel). Если для голосового общения, функция активности микрофона важна для экономии трафика, то при записи речи или песни это может навредить - запись будет не идентична выступлению.
- Необходимо ограничить время записи (например, по таймауту), иначе найдутся охотники заполнить дисковое пространство сервера бесполезным мусором. Хотя, конечно, правильнее это ограничение сделать на сервере.
В итоге, на выходе у меня получились две версии проигрывателей звука в форматах MP3 и FLV (можно было сделать универсальный, но времени не хватило) и рекордер звука.
На локальном Red5, под Windows, это хозяйство работало на ура. Однако, когда потребовалось поставить Red5 под FreeBSD (если не ошибаюсь), у админов возникли серьезные проблемы с настройкой нашего приложения. Подробности мне не известны, знаю только, что у них всё заработало за день до моего прилета (т.е. возились 2 недели :) ).
Такова уж особенность Open Source - конфигурация представляет собой темный лес xml-файлов, а нормальные инструменты по настройке, так чтобы галочку поставил и всё заработало - отсутствуют. Вот и выбирайте - платить за софт, или платить специалистам за настройку бесплатного софта. Но, слава Богу, всё это уже не моя забота.
* * *
Неплохая статья в тему:
Урок. Вебкамеры с Flex 3. Часть 2
воскресенье, Март 22, 2009
mx:Pause vs flash.utils.Timer. Flex
Легкое определение переменных в MXML значительно упрощает разработку:
- позволяет легко и наглядно определять их свойства и обработчики событий
- автоматически делает переменные Bindable
- позволяет их связать с другими переменными и выражениями
- делает наглядным код
Но, на сколько я понял, не все классы можно представить в декларативном виде.
Например, класс flash.utils.Timer невозможно представить в виде MXML. Компилятор выдает ошибку "Неправильное количество аргументов". Всё от того, что конструктор этого класса содержит один обязательный параметр. Как его указать, и возможно ли это, увы, я не понял.
Но есть простое решение - использовать другой класс, хорошо подходящий по своему функционалу - mx.effects.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, понимаю, что есть необходимость правильно его комментировать. Просматривая листинги 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:
<!-- 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
Для начала, несколько заметок:
- Перенос строки в строках MXML: символ 
 - мелочь, а приятно.
- Загрузка сложных Flash-приложений в SWFLoader должна производиться в выделенный домен приложения, т.к. могут быть конфликты классов:
. . .
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-ом. Он не дает назвать разные стили разными именами (как это можно сделать в первом случае), теряет одно из начертаний (жирный шрифт просто не выводится). Возможно, это из-за особенностей конкретных шрифтов, но первый метод срабатывает гораздо стабильнее.
{
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 марта!
суббота, Февраль 28, 2009
Модули vs. компоненты. Flex
В моем опыте Flex-разработки наступает новая стадия взросления.
Ну, условно, у себя я уже могу выделить две стадии (не считая робкого потрагивания и любопытного пощупывания):
- Код представляет собой голый MXML, на котором и построено всё приложение. Крупные вспомогательные структуры классов и обильные инклюды. В решении относительно сложных задач (либо совсем простых) я полагался только на pure AS проекты и компоненты.
- Приложение делилось на компоненты (MXML компоненты), каждое окошко, каждый функционально законченный экран были компонентами. Обильное использование компонентов значительно упростило разработку приложений и внесло строгость и порядок, несмотря на довольно пространный список в Components -> Custom.
Кроме того, такой подход значительно ускоряет разработку, уже засчет того, что визуальный редактор теперь не должен перерисовывать каждый раз всё приложение (а делает он это отнюдь не быстро).
Сегодня в разработке проект, который так и напрашивается на использование модулей. В общих чертах - это небольшой промо-сайт с тремя основными разделами. Три этих раздела мы и оформим как модули.
Руки так и зачесались попробовать создать модуль в FB. Открываю проект, создаю New->MXML Module. Открывается новый файл mxml, в который я добавляю, к примеру, кнопку, и текстовое поле. Сохраняю. В окошке Components->Custom появился новый компонент, по имени модуля.
Проведем эксперемент. Перетащим его в окно основного приложения. Всё как с обычным компонентом. Компилирую. Замечательно: скомпилировалось два swf - один с именем приложения, другой - с именем модуля.
Для любопытства, копируем один только swf приложения и запускаем: видим кнопку и текстовое поле. Как я и предполагал, наш модуль включен в состав swf приложения как обычный компонент.
Запустим swf модуля. Сразу получаем ошибку "Не удалось найти класс mx.core::SpriteAsset." И не мудрено - модуль не содержит классов, включенных в основное приложение.
Но нам-то от модуля нужно что? Чтобы он не был включен в приложение, а подгружался в процессе работы приложения. Хотя, возможность использовать модуль как обычный MXML-компонент тоже надо отметить.
Ознакомимся вкратце с документацией: Creating Modular Applications. Здесь всё, на достаточно понятном языке, достаточно подробно разжевывается.
Итак. Нас интересовали загрузка модуля и ее мониторинг.
Пожалуйста, смотрим: Loading and unloading modules. За загрузку отвечает класс ModuleLoader. Любопытно, что это наследник VBox. Непонятно, почему именно VBox, а не, к примеру, HBox. Уж я-то вообще ожидал увидеть в этой роли SWFLoader. Ну да ладно. Убираем из кода приложения модуль и вставляем ModuleLoader. Кстати, он присутствует в палитре компонентов Layout.
Вписываем в свойство url имя файла модуля. И что особенно приятно, визуальный редактор сразу отобразил содержимое модуля. Вводим имя другого модуля - пожалста! Отображается другой модуль. Запускаем - всё замечательно отображается и работает.
Теперь по поводу мониторинга. Смотрим Using ModuleLoader events. ModuleLoader генерирует следующие события: setup, ready, loading, unload, progress, error, и urlChanged. Но позвольте! Если progress мы наблюдаем в этом списке, то почему нет open и complete? Что помешало вместо loading генерировать open, а вместо ready - complete?
При таком раскладе, если я использую ProgressBar в режиме mode="event", загрузка успешно мониторится, но ProgressBar не генерирует событие complete, что в некоторых случаях было бы полезно. Ну что ж, никто не мешает нам устранить этот недостаток, создав своего потомка ModuleLoader. Дело поправимое.
Есть еще один компонент, управляющий загрузкой модулей: ModuleManager. Этот класс, как нам обещают, предоставляет больше возможностей по управлению загрузкой модулей чем предыдущий. Но при этом, как утверждается, техника его использования является менее абстрактной чем работа с ModuleLoader.
Да, разработчики Flex не перестают меня удивлять. ModuleManager наследуется от Object и содержит всего два статических метода, что ввело меня в небольшой ступор. Однако, после изучения предложенных примеров, всё встало на свои места. Метод ModuleManager.getModule возвращает объект, удовлетворяющий интерфейсу IModuleInfo, уникальный для каждого управляемого модуля. Этот объект, в дальнейшем, можно использовать для загрузки модуля (метод load) и мониторинга событий загрузки, а затем, для инстанцирования модуля (через свойство factory).
Более подробное изучение этого класса, погружает нас в глубины Flex, что в мои планы пока совсем не входит. Вот уж, другими словами и не скажешь - все намного менее абстрактно. И больше подходит для решения специфических задач.
Ну-с, добро пожаловать в мир модульных приложений. Начинаем действовать!
* * *
Впечатления. Работа с модулями не разочаровала: стабильно и надежно. Единственные проблемы, с которыми я столкнулся:
- Внедрение шрифта. CSS с внедрением TTF-шрифта определяется в главном приложении. Подгружаемые модули успешно используют эти шрифты. Но, возникла проблема с внедряемыми SWF, в которых используется другой шрифт. При вводе в динамические поля, ничего не отображалось. Тогда я внедрил этот шрифт в модуль. Шрифт стал отображаться. Но, что интересно, когда я собрал Release Build, шрифт опять перестал отображаться. Пришлось оставить Debug-версию модуля. Но это повлекло за собой следующую проблему.
- Если основное приложение собрано в Release Build, оно некорректно подгружает модуль, собранный в Debug Build. Я думаю, что вообще, по-отдельности модули лучше не обновлять. Я обратил внимание, что размер SWF-файла модуля даже при небольших изменениях, при перекомпиляции заметно меняется. Поэтому, следует, наверное, соблюдать осторожность при компиляции и обновлении модулей.
среда, Февраль 25, 2009
В цинковом гробу. Перетаскиваемые окошки
Во-первых, сразу зарекаюсь - Мак только для мак-девелоперов. Без меня. Такого количества проблем я уже давно не встречал. Возможно, основным виновником их является Zinc, который я смело могу назвать УСЛОВНО кросс-платформенным.
Что сказать про Zinc 3.0? Под красивым брендовым дизайном, симпатичной оболочкой, красивыми заголовками скрывается довольно ограниченный функционал, убогая документация и море непонятностей, глюков и граблей.
В данном посте, приведу один пример, как простая задача решается через "заднее место".
Нужно, чтобы окошко нашего виджета перетаскивалось мышкой за специальную панельку. В документации, нам рассказывают про это так: Creating a Draggable Form. И что мы получаем? Да полный отстой. Мышь теряет окошко, потом, при наведении вдруг опять подхватывается, уже без нашего на то соизволения, в общем, ужас. Как это победить? Нужно повесить 3 обработчика мыши на панельку и сделать это вот как:
dragNDropRenderer.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
dragNDropRenderer.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
dragNDropRenderer.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
. . .
private function rollOverHandler(event:MouseEvent):void {
mdm.Forms.getFormByName("MainForm").startDrag();
}
private function rollOutHandler(event:MouseEvent):void {
if (!event.buttonDown) {
mdm.Forms.getFormByName("MainForm").stopDrag();
}
}
private function mouseUpHandler(event:MouseEvent):void {
mdm.Forms.getFormByName("MainForm").stopDrag();
}
Конфликт событий мыши распространяется только на нажатие. Отпускание, вполне корректно работает. Кстати, при перетаскивании, обрабатывать нажатие вообще не советую - реакция совершенно непредсказуема. Таким образом, мы отрубаем перетаскивание пир отпускании кнопки, а при отведении мыши от панели проверяем, отпущена ли в данный момент кнопка. Если отпущена - останавливаем перетаскивание. Если нет - ни в коем случае этого не делаем - мышь потеряет окошко в момент таскания.
Другие баги - почему под MacOX криво работает LocalConnection, или не открываются Zinc-ом новые окошки, и не распарсивается полученный с сервера XML, мне еще предстоит выяснить. Чувствую, это будут "приятные" сюрпризы.
* * *
В обработчик отпускания мыши добавлена проверка на отсутствие мыши в области панели.
* * *
Продолаются жалобы. Под MACOX не удалось (под Windows всё Ок) передать на сервер запрос с GET-параметрами (URLRequest). Запрос проходит, параметры не передаются. Не имел возможности выяснить, виновен ли в этом Zinc или админы сервера. В итоге, стали передавать параметры через POST, что сразу дало положительный результат.
* * *
Прозрачность. Плашка прозрачностью 1% ложится поверх поверхности со сложным изображением сложной прозрачности. Окно приложения устанавливается прозрачным. И что вы думаете? Эта плашка видна. Она как бы добавляет прозрачности подлежащему изображению. То же самое можно сказать, если поверх ложится растр с прозрачностью. Прозрачная область растра становится видна.
Выход - не допускать "недопрозрачностей", обтравливать растр во флэше.
* * *
Всё. Финиш. Zinc и MacOX - больше несовместимые понятия. Делаю полноэкранное приложение. В окне приложения, справа, где-то на 1/4 ширины, существует "мертвая" зона, в которой мышь перестает оказывать воздействие на интерактивные flash-элементы. Нет, нельзя сказать что совсем не оказывает. После 5-10 нажатий, кнопка срабатывает. Но это же бред. Элементраный пример - делаем во flash плашку размером с экран (1280x800), вставляем SWF в гроб, билдим и наблюдаем эту границу. Кошмар. Под Windows и под MacOX в Safari работает превосходно.
Сделать многооконное приложение с Zinc также не получилось - опять же проблемы с мышью. Интерактив не откликается.
среда, Февраль 11, 2009
Как отображать шрифт без сглаживания (bitmap text) во Flex
Если во Flash CS, мы можем просто так взять, да и указать тип рендеринга шрифтов "Bitmap text" (без антиалиасинга) и иметь полноценное отображение текста с неполной прозрачностью, наклоном и т.п., то во Flex мы можем только управлять параметрами сглаживания шрифта, или не внедрять шрифт совсем (тогда о прозрачности не может быть и речи).
Но если мне, всё-таки, нужен именно несглаженный полупрозрачный шрифт?
Проблема решается, как и многие другие во Flex, через "одно" место.
В лайфдоках, находим такую статейку: Embedding fonts from SWF files. Здесь рассказывается вообще о внедрении шрифта с использованием Flash CS. Пользуясь таким методом, можно внедрить любой шрифт, поддерживаемый Flash CS.
Однако, здесь не сказано ни слова про внедрение шрифта Bitmap text (без антиалиасинга). И вообще, в документации Flex, про это как-то совсем ничего нет. А ведь это странно и непонятно - Flex-приложения как раз направлены на отображение данных, а всем известно, что данные отображаются лучше несглаженным шрифтом. К чему тогда все эти эффекты с фэйдом и прочими трансформациями, если невнедренный шрифт их не отображает.
Нам на помощь приходит статья всеобщего друга всех флэшеров GSkinnerа Bitmap Fonts in Flex (via Flash). Правда, в ней всё несколько усложнено, но, подозреваю, что это из-за того, что писалось это для Flex 2. Ну а мы то уже на Flex 3, поэтому всё немного проще. Идея в том, что шрифт, внедренный как Bitmap text, меняет название в некую неприглядную форму, типа "Tahoma_12pt_st".
Итак, к чему мы пришли (опишу вкратце мои действия):
1. Создаем SWF-файл fonts.swf (можно любой другой). Версия Flash 9, ActionScript 3.0. Во всех статьях создается файл версии Flash 8, но, почему-то, у меня такой файл Flex не подцеплял/ - не может оттранскодить.
2. Создаем текстовые Dynamic-поля, в каждое из которых добавляем хотя-бы один символ нужного нам начертания. Соответственно, если все начертания нам нужны, то будет 4 текстовых поля: Normal, Bold, Italic, Bold Italic.
3. Устанавливаем размер шрифта, который мы будем использовать в Flex-приложении. Дело в том, что в приложении, корректно может быть отображен только один размер внедренного шрифта - тот который мы сейчас укажем. Если указать другой размер, шрифт некрасиво размажется. Я указываю 12.
4. Указываем в Embed... диапазоны, которые нам надо внедрить.
Кстати, можно пойти другим путем и, вместо текстовых полей, создать в библиотеке 4 символа-шрифта (New font...) и всё будет точно так же, за исключением того, что шрифт внедрится весь - без ограничений, что плохо для объема.
5. Компилируем и добавляем в папку проекта.
6. В CSS файле проекта (я отвел для этого специальный файл fonts.css) указываем следующий код:
@font-face {
src: url("file://./assets/fonts.swf");
fontFamily: "Tahoma_12pt_st";
fontStyle: normal;
fontWeight: normal;
}
@font-face {
src: url("file://./assets/fonts.swf");
fontFamily: "Tahoma_12pt_st";
fontStyle: normal;
fontWeight: bold;
}
@font-face {
src: url("file://./assets/fonts.swf");
fontFamily: "Tahoma_12pt_st";
fontStyle: italic;
fontWeight: normal;
}
@font-face {
src: url("file://./assets/fonts.swf");
fontFamily: "Tahoma_12pt_st";
fontStyle: italic;
fontWeight: bold;
}
7. Теперь, мы можем в любом стиле указать:
fontFamily: "Tahoma_12pt_st";
fontSize:12px;
fontAntiAliasType:normal;
}
Важно указать параметр fontAntiAliasType:normal. Если этого не сделать, шрифт в некоторых случаях будет отображаться размыто, а если это выделяемый текст - при его выделении будет твориться что-то невообразимое.
Ну и конечно, не забыть уточнить размер шрифта - иначе всё поедет.
8. Ну и собственно, применяем этот стиль к компоненту:
width="100%"
styleName="Regular"
>
<mx:htmlText><![CDATA[<b>Максимальный</b> размер загружаемого файла — 3 Мб. Допустимые форматы: GIF, JPG, BMP, TIFF, PNG.]]>></mx:htmlText>
</mx:Text>
Если нам понадобится шрифт другого размера - уж не поленитесь, повторите всё со второго пункта - так уж положено.
* * *
Да, забыл добавить про то, что здорово помогает в разборках со шрифтами такой кусочек кода (взят из статьи):
var fontList:Array = Font.enumerateFonts(false);
for (var i:uint=0; i<fontList.length; i++) {
trace("font: "+fontList[i].fontName);
}
