среда, Май 21, 2008

Хороший стиль Flex-программирования. Именование

Хочется кодить лучше, правильнее, грамотнее, понятнее, по правилам, ... и чтобы не было стыдно кому-нибудь показать.

Adobe любит нас, и создал для нас небольшой свод правил, который поможет не только лучше понимать Flex SDK, но и нам самим сделать свой код лучше:
Flex SDK coding conventions and best practices

Итак, сегодня мы поговорим об именовании (Naming).

Именование

Наши стандарты именования соответствуют стандартам ECMAScript и Flash Player 9.


Использование сокращений

Вообще, лучше их избегать. Например:
можно: calculateOptimalValue(), нельзя: calcOptVal().

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

Однако, есть общепринятые сокращения:
acc = accessibility (ButtonAccImpl)
auto = automatic (autoLayout)
eval = evaluate (EvalBindingResponder)
impl = implementation (ButtonAccImpl)
info = information (GridRowInfo)
num = number (numChildren)
min = minimum (minWidth)
max = maximum (maxHeight)
nav = navigation (NavBar)
regexp = regular expression (RegExpValidator)
util = utility (StringUtil)

Существуют и другие сокращения. Если мы не догадаемся об их смысле, придется поискать их применение в исходном коде. Или подумать еще разок.

Иногда можно встретить отсутствие логики в применении сокращений. Например, horizontalScrollPolicy и verticalScrollPolicy, но HBox и VBox.


Использование аббревиатур

Аббревиатуры довольно часто используются во Flex: AIR, CSS, HLOC, IME, MX, MXML, RPC, RSL, SWF, UI, UID, URL, WSDL, XML.

Аббревиатуры всегда пишутся либо заглавными, либо строчными буквами:
можно: SWF, swf, нельзя: Swf

Строчные буквы используются только в том случае, когда весь идентификатор именуется аббревиатурой, либо идентификатор начинается с аббревиатуры и он должен начинаться со строчной буквы.

Например:
можно: CSSStyleDeclaration, IUID, uid, IIME, imeMode.


Разделение слов

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

В некоторых местах может встретиться нарушение этого правила: комбинация слов становится целым словом: dropdown, popUp, pulldown.


Имена, содержащие тип

Если в имени нужно указать тип, поместите его, не сокращая, в конец имени. Откажитесь от использования суффиксов типа "_mc" по старой традиции ActionScript 1.

Например:
можно: border, borderSkin, or borderShape, нельзя: border_mc

Часто лучшим именем для объекта является имя его типа в отличающемся регистре (не соглашусь, т.к. смысловой нагрузки никакой - годится только для тестов и примеров):
var button:Button = new Button();


Имена пакетов

Начинаются со строчной буквы и для разделения слов используется регистр: controls, listClasses.

Имена пакетов должны быть всегда существительными или отглагольными -ing формами (герундий), но не глаголами, прилагательными или наречиями.

Пакет, включающий множество схожих элементов должен именоваться общим названием этих элементов во множественной форме: charts, collections, containers, controls, effects, events ...

Обычно, -ing формами именуются пакеты, реализующие концепции (я бы сказал процессы): binding, logging, messaging, printing ...
С другой стороны, это могут быть существительные, выражающие концепцию: accessibility, core, graphics, rpc ...

Пакет, содержащий классы, которые обеспечивают работу компонента FooBar, должен называться fooBarClasses.


Имена файлов

Имена файлов импортируемых API должны соответствовать определениям public API. Но для include-файлов (файлов фрагментов кода) это правило не применяется.

Имя include-файла начинается с заглавной буквы и разделение слов в имени осуществляется регистром: BorderStyles.as, ModalTransparencyStyles.as

Имена файлов ресурсов пишутся строчными буквами, а слова разделяются символом подчеркивания: icon_align_left.png


Имена пространств имен

Именуются строчными буквами с разделением слов символом подчеркивания: mx_internal, object_proxy.


Имена интерфейсов

Начинаются с заглавной "I", разделение слов в имени осуществляется регистром: IList, IFocusManager, IUID.


Имена классов

Начинаются с заглавной буквы, разделение слов в имени осуществляется регистром: Button, FocusManager, UIComponent.

Подклассы Event именуются FooBarEvent.
Подклассы Error именуются FooBarError.
Подкласс EffectInstance, ассоциированный с эффектом компонента FooBar именуется FooBarInstance.
Подклассы Formatter именуются FooBarFormatter.
Подклассы Validator именуются FooBarValidator.
Классы скинирования компонентов именуются FooBarBackground, FooBarBorder, FooBarSkin, FooBarIcon, FooBarIndicator, FooBarSeparator, FooBarCursor, и т.д.
Классы утилит именуются FooBarUtil (не FooBarUtils - пакет может именоваться во множественном числе, но класс - только в единственном).
Общепринято базовый класс именовать FooBarBase: ComboBase, DateBase, DataGridBase, ListBase.


Имена событий

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: move, creationComplete.


Имена стилей

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: color, fontSize.


Значения строковых свойств

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: "auto", "filesOnly".


Имена констант

Именуются полностью заглавными буквами с разделением символом подчеркивания: OFF, DEFAULT_WIDTH.

Если константа строковая, слова в имени константы должны соответствовать словам в строке ее значения:
public static const FOO_BAR:String = "fooBar";


Имена свойств (переменные и установщики)

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: i, width, numChildren.

Используйте "i" для имени переменной цикла "for", и "n" для имени верхнего предела цикла.
Используйте "j" для имени переменной вложенного цикла "for", и "m" для его имени верхнего предела цикла:
for (var i:int = 0; i < n; i++)
{
for (var j:int = 0; j < m; j++)
{
...
}
}

Используйте "p" для имени переменной цикла "for..in":
for (var p:String in o)
{
...
}

Бывают ситуации, когда в классе необходимо переопределить установщик, но предполагается, что базовый установщик будет продолжать использования. Для сохранения базового установщика, необходимо создать новый установщик, имя которого должно формироваться путем добавления к имени базового установщика символа "$". Полученный установщик нужно определить как "final". Он не должен ничего делать, кроме вызова базового установщика. Напимер, нам нужно перекрыть установщик numChildren, но при этом сохранить возможность вызывать базовый super.numChildren - для последнего мы создаем следующее:
mx_internal final function get $numChildren():int {
return super.numChildren;
}


Переменные для хранения данных установщиков

К имени установщика добавляется символ подчеркивания "_":
для foo имя переменной _foo.


Имена методов

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: measure(), updateDisplayList().

Имена методов должны быть всегда глаголами.

Лучше не использовать методы без параметров подобно getFooBar() или setFooBar(). Вместо них используйте установщики. Однако, если getFooBar() - медленный метод, требующий больших вычислений, лучше его назвать findFooBar(), calculateFooBar(), determineFooBar(), и т.д., выражая его суть, а не то, что он способен возвращать что-либо.

Аналогично установщикам, если базовый метод перекрывается, но требуется сохраненить доступ к нему, создается метод с именем базового метода, перед которым добавляется символ "$":
mx_internal final function $addChild(child:DisplayObject):DisplayObject
{
return super.addChild(child);
}


Обработчики событий

Имя обработчика события формируется путем добавления к имени типа события слова "Handler": mouseDownHandler().

Если обработчик предназначен для событий, отправленных субкомпонентами (т.е. не this), перед именем обработчика, сформированного по вышеописанному правилу, добавляется имя субкомпонента, отделенное символом подчеркивания "_": textInput_focusInHandler().


Имена аргументов

Всегда используйте для установщиков имя аргумента "value":
можно - public function set label(value:String):void
нельзя - public function set label(lab:String):void
нельзя - public function set label(labelValue:String):void
нельзя - public function set label(val:String):void

Всегда используйте для обработчиков событий имя аргумента "event":
можно - protected function mouseDownHandler(event:Event):void
нельзя - protected function mouseDownHandler(evt:Event):void


Имена пакетов ресурсов

Если пакет рессурсов содержит ресурсы для определенного пакета классов, имя пакета рессурсов должно быть таким же: controls, {formatters}}, validators.


Имена идентификаторов ресурсов

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: pm, dayNamesShort.


Прочая терминология

Избегайте использование слова "object", т.к. оно вносит неопределенность.

Слово "item" - элемент данных и никогда DisplayObject.

Слово "renderer" - это DisplayObject, который отображает элемент данных.

Слово "type" означает тип AS3, используйте вместо него слово "kind".

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

Следующий раздел "Language Usage" разберем в ближайшее время.

вторник, Май 20, 2008

Немногодетный Loader. AS3

Loader может хранить всего один визуальный объект. Попытка загрузки в один и тот же Loader более одного раза заканчивается генерацией исключения.
Кроме того, как сказано в хелпе, вызов методов: addChild(), addChildAt(), removeChild(), removeChildAt(), and setChildIndex(). также закончится неудачей.

Но Loader любит своего единственного малютку, и если мы захотим покуситься на него, нам придется убить самого Loaderа.

понедельник, Май 19, 2008

Куда переползли FlashVars. AS3

В ActionScript 2.0 Migration не указывается куда делись FlashVars в AS3.
Поэтому простым поиском в Help-е находим что:
Класс flash.display.LoaderInfo имеет свойство parameters:Object в которое складируются в виде ассоциативного массива параметры URL-строки SWF-файла и параметры строки FlashVars, которые действительны только для главного SWF.
Добраться до него легко, используя свойство основного flash-клипа loaderInfo:

for (var name:String in this.loaderInfo.parameters) {
trace(this.loaderInfo.parameters[name]);
}

пятница, Май 16, 2008

Из Flex Builder во FlashDevelop

Судьба распорядилась так, что временно приходится перебросить разработку Flex-проекта с Flex Builder на FlashDevelop. Заодно проверить, как последний с этим справится.

Установил последнюю версию FlashDevelop 3.0.0 Beta7. Теперь в пустом окне "Project" есть очень заманчивая кнопка "Import a Flex project", которой я немедленно и воспользовался. FD быстренько подхватил папку Flex-проекта и поместил в нее свой файл *.as3proj. Теперь можно спокойно работать как с обычным FD-проектом.

Первая нехорошая особенность - кодировка файлов, созданных FB распознается не правильно, и приходится постоянно корректировать кодировку файлов.

При компиляции проекта, получаем ексцепшн: "Error: null java.lang.NullPointerException...
Build halted with errors (mxmlc)"
. Хорошенькое дело. Ищем причину. Аналогичная ошибка имеет место не только у меня одного. Причина кроется в какой-то ошибке при внедрении шрифта. Как только я убрал

@font-face {
font-family: localArial;
font-weight: normal;
src: local("Arial");
...
}

Ошибка пропала, но такой вариант совсем негодится. Жаль, но на этом прийдется остановиться, пока не узнаю, как побороть этот недостаток...

четверг, Май 15, 2008

Откуда начинается XML. AS3

Всё время возникает путаница.
Есть XML:
<?xml version="1.0" encoding="utf-8" ?>
<data>
<item>Раз</item>
<item>Два</item>
</data>

После его загрузки, он отправляется в переменную:

private var __xml:XML;

. . .

private function onComplete(event:Event):void {
this.__xml=new XML(event.target.data).normalize();
}


Далее, мы хотим получить первый элемент списка и по логике (старой доброй логике AS2) пишем:
trace(this.__xml.data.item[0]);
Но получаем эксцепшном по лбу: "Error #1010: A term is undefined and has no properties.".
Потому что надо так:
this.__xml.item[0]
Контейнерный тег data (вместо data может быть любое другое слово) и будет являться нашим this.__xml.

Делаем прелоадер для Flex-приложения. Продолжение. AS3

Поиск привел к статейке: Making a Cooler Preloader in Flex: Part 1 of 3.

Доступно, с примерами раскрывается процесс создания собственного прелоадера для Flex-приложения. Пожалуй, выделю основные положения, опустив приступы остроумия автора :).

Введение.

Для создания собственного прелоадера для Flex, необходимо всего два шага:

  1. Расширить класс DownloadProgressBar.
  2. Указать Application, в свойстве preloader, этот новый класс.

Или подробнее:

  1. Создать MovieClip, содержащий 100 фреймов.
  2. Указать ему Linkage ID без имени класса.
  3. Скомпилиоровать SWF.
  4. Написать собственный прелоадер, использующий созданный Flash MovieClip.
  5. Указать в mxml-коде, в теге mx:Appliction свойство preloader с именем созданного класса.

Создание Flash клипа

Автор предлагает создать 100-фреймовый клип с анимацией отображения прогресса загрузки, и добавляет в него dynamic TextField. Полученный клип он убирает в другой клип-контейнер, содержащий таймлайн с плавной анимацией появления и скрытия прелоадера. При этом необходимо дать имена всем клипам, участвующим в доступе к 100 фреймам и текстовому полю, для того, чтобы затем обратиться к ним по принципу “dot dot down” (это оказывается у нашего брата флэшера существует такой сленг, означающий "спуск" по дереву клипов к объекту, с которым нужно взаимодействовать).

Важно: Никаких скриптов в таймлане не пишем.

Затем - что еще любопытнее - автор экспортирует всё это хозяйство во Flash 8, AS2 или AS1. Как он это объясняет, если делать прелоадер под AS3, то потребуется писать класс для прелоадера, а это повлечет за собой массу работы и дополнительного объема к файлу. Наш прелоадер должен быть простым и максимально легким.

Итак, экспортируем в Flash 8, а в Linkage указываем идентификатор и ставим 1-ю и 3-ю галки. Flex должен увидеть наш Linkage ID, когда мы внедрим наш SWF.

Класс прелоадера

Создаем класс, экстендим его от mx.preloaders.DownloadProgressBar. Внедряем в него Flash контент мета-тегом [Embed(source="... /preloader.swf", symbol="Preloader")]. В конструкторе создаем и добавляем в дисплей-лист объект прелоадера. Затем, перекрывая свойство-установщик preloader, назначаем обработчики событий загрузки.

Важный момент: для того, чтобы организовать плавное скрытие прелоадера, нобходимо прибегнуть к следующему трюку. Мы предусмотрели в клипе-контейнере анимацию скрытия прелоадера (которая заканчивается, к примеру, на 20-м фрейме, а фрейм 21 пуст) . По событию onFlexInitComplete, мы добавляем скрипт в фрейм 21: clip.addFrameScript(21, onDoneAnimating);. Наш скрипт останавливает анимацию прелоадера и генерирует событие Event.COMPLETE.

Важное добавление в комментариях: В коде обработчика onDoneAnimating необходимо отписаться от всех событий, на которые подписался класс прелоадера.

Пример и исходники прилагаются: Example Source ZIP.

Попробуем, как это работает на практике.

* * *

Попробовал. Работает. Но пришлось убить часа два на следующую проблему: В примере в главном клипе скрипт устанавливается во фрейм 21. Я создал клип с таймлайном в 21 фрейм. Не заработало. Скачал пример: Download Preloader FLA. Заработало. Подменил своим - не заработало (клип крутится по кругу, но не стартует скрипт).

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

* * *

Выяснилось, что не обязательно использовать версию Flash8 AS2. Можно указать и Flash 9 с AS3. Однако как ни крути, скрипты из тайм-лайна вызываться не будут. В целях снижения объема SWF-файла лучше всеже использовать более старые версии - лучший результат показал Flash 6/7 AS1/AS2, но не намного.

* * *

Еще одна важная фича - фон приложения на этапе предзагрузки. Живой Flex подсказывает нам, что это можно сделать двумя путями:

  1. Добавить в пункте "Additional compiler arguments:" строку:
    -default-background-color #336699
  2. Указать цвет фона в параметрах тегов Object и Embed при внедрении флэш-приложения в HTML-страницу:

понедельник, Май 12, 2008

Баг при выходе пользователя из комнаты. AS3. SmartFoxServer

Генерируется исключение (TypeError: Error #1009: Cannot access a property or method of a null object reference.) в модуле it.gotoandplay.smartfoxserver.handlers.SysHandler, версия модуля 1.2.0, в строке 348:

var uName:String = theRoom.getUser(userId).getName()

Генерируется клиентом при выходе из комнаты какого-либо другого пользователя, при условии, что до этого был вызван метод this.__sfs.getRoomList(); и обработано событие onRoomListUpdate.
Коррекция ошибки заключается в проверке на null метода theRoom.getUser(...):
var uName:String = "";
if (theRoom.getUser(userId)!=null)
uName = theRoom.getUser(userId).getName()

Ненавижу корректировать чужой код, но тут другого выхода не вижу.

среда, Май 07, 2008

Принцип перебора for .. in . AS3

Столкнулся с интересной проблемой.
Есть ассоциативный массив:
static private const distribution:Object = {item1:2,item2:2,item3:2};
Осуществляю перебор элементов:


. . .

this.__distribution=distribution;

. . .

for (var name:String in this.__distribution) {
count=int(this.__distribution[name]);

. . .

}

Ожидаем, что элементы массива будут перебираться по каком-нибудь вменяемому принципу - например, в AS2 перебор осуществлялся от последнего к первому.
Однако, порядок перебора свойств оказывается совершенно не очевидным.
Иногда перебор начинается с item2, иногда с item3. Особенно это очевидно при запуске приложения на разных компьютерах.

Ничего удивительного, в хелпе об этом так и сказано:
Iterates over the dynamic properties of an object or elements in an array and executes statement for each property or element. Object properties are not kept in any particular order, so properties may appear in a seemingly random order.

Именование безымянных клипов. AS3

Есть SWF с кучей клипов.
Он подгружается во Flex-приложение.
Flex-приложение перебирает содержимое SWF:

for (var i:int=0; i<this.__template_container.numChildren; i++) {

. . .

}

Клипам, которым небыли назначены имена, Flash присвоил имена по умолчанию типа instanceXXX. И что самое интересное, этот XXX не поддается никакому очевидному принципу. Либо на разных компьютерах, либо в разных версиях запуска приложения, одним и тем же клипам назначаются разные числа.

Поэтому, нельзя надеяться на имена "по умолчанию". Если раньше, в AS2 можно было быть уверенным, что где-то на сцене присутствует instance0, то теперь об этом лучше даже не думать. Конечно, потребность в таких именах невелика, но порой может сбить с толку, например при отладке.

четверг, Май 01, 2008

FLVPlayback и ошибка 1000. AS2

Столкнулся с тупейшей проблемой.
Есть FLVPlayback с именем video (со свойствами по умолчанию).
По клику на кнопку b1 указывается:
video.contentPath="URL существующего файла";
По клику на кнопку b2 указывается:
video.contentPath="URL несуществующего файла";
По клику на b1 видео воспроизводится. По клику на b2, видео, если оно воспроизводилось, останавливается, если нет - ничего не происходит. Теперь, при повторном нажатии на b1, мы ожидаем снова увидеть наше видео, но.... получаем ошибку:
1000: Unable to make connection to server or to find FLV on server
Что бы я не делал - вызывал разные методы типа stop/play, менял свойства компонента - всё безрезультатно.

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

Пока вижу одно решение - перед запуском видео, проверять наличие файла.

пятница, Апрель 25, 2008

"Ручной" cacheAsBitmap. AS2

Для оптимизации быстродействия flash-приложения, мы часто прибегаем к кэшированию:

  • Устанавливается галка "Use runtime bitmap caching" - не очень удобный способ.
  • В as-коде клипа пишется cacheAsBitmap=true - удобно, но при кэшировании мы теряем сглаживание. То есть перемещаться наша картинка будет "рывками".

Для того, чтобы лучше контролировать наше кэширование, попробуем сделать его вручную. Для этого сделаем снимок нашего клипа, удалим всё его содержимое и вставим снимок обратно в клип.

Код можно посмотреть здесь: "Ручной" cacheAsBitmap. AS2.

Особенности:

  • Ограничения по размерам не более 2880х2880.
  • Все визуальные элементы должны быть клипами, иначе не всё удалится, что ухудшит производительность.
  • Несмотря на выставленные параметры в attachBitmap pixelSnapping="auto" и smoothing=true, каринка всё равно продолжает перемещаться рывками. Можно поменять pixelSnapping на "never" - не имеет значения. Однако, как только была добавлена строчка:
    this._height-=0.5;
    сразу включилось сглаживание и картинка поплыла плавно. Почему именно 0.5? Это минимальное искажение было установлено экспериментально для моей конкретной задачи. Для разных размеров может быть разное значение. Главное, чтобы произошло незначительное искажение хотя бы одного из размеров (горизонтального или вертикального).

Возможно есть более элегантное решение.

четверг, Апрель 17, 2008

Изображения в текстовом поле. AS2

HTML-размета текстового поля, для Flash не менее 8-й версии, поддерживает динамическую загрузку изображений, путем указания тега <IMG>.

Поддерживаются следующие параметры тега:
src - URL изображения (JPEG, GIF, PNG, или SWF). Изображения не отображаются до тех пор, пока полностью не загрузятся.
id - имя клипа, который будет создан FP и в который загрузится изображение. Подробнее здесь.
width - ширина изображения в пикселах.
height - высота изображения в пикселах.
align - горизонтальное выравнивание изображения. возможны значения "left" и "right". По умолчанию - "left".
hspace - отступы от краев изображения по горизонтали. По умолчанию "8".
vspace - отступы от краев изображения по вертикали. По умолчанию "8".

Заметки:

  • Текст обтекает изображение по принципу, общепринятому в HTML-разметке.
  • Пока вертикальные размер и отступы изображения умещаются в расстояние от верха строчки до ее низа, нижняя строчка не смещается. (Например, при размере шрифта 11px и Line spacing 2pt, максимальный размер изображения по вертикали - 15)
  • Если перед тегом IMG есть текст (даже просто пробел), изображение вставляется в строку, расположенную ниже той, куда был вставлен тег.
  • Мы никогда не сможем поместить изображение в центр текстового блока.
  • Если указать только width или только height, с изображением ничего не произойдет.
  • Если встатвить подряд несколько абсолютно схожих тегов IMG, изображения выведутся в одну точку. Однако, стоит изменить один из параметров, как все последующие изображения выводятся ниже строкой.
  • Если задать подряд два тега IMG у которых параметры align установлены соответственно в "left" и "right", второе изображение всё равно будет строчкой ниже. А если второй тег поставить в конец строки - его изображение спустится уже на две строчки.
  • Еще одна ужасная особенность. Если свойство wordWrap = false, и тег IMG стоит первым, изображение просто не выводится.

Можно и дальше продолжить пытки TextField, и выявить новые фичи (читай баги). Но уже и так понятно, что нормальная вставка изображений в текст TextField (без хорошего бубна) невозможна.