Существует много хороших ресурсов, где рассказано как создавать скины для Flex приложений. Flex поддерживает два подхода: графическое и программное скинование. Графическое скинование – это создание графических наборов во Flash, Photoshop, Fireworks и встраивание их во Flex приложение. Программное скинование – создание ActionScript класса, который определяет скин виджета. Как вы уже догадались, графическое скинование проще, а программное – мощнее.
Единственным недостатком обоих подходов является то, что ресурсы для скинования (SWF/PNG/GIF/ и т.д. для графических скинов, AS классы для программных скинов) должны быть доступны во время компиляции. Или не должны? Позже я объясню, как загружать скины в run-time (с помощью готового примера).
Чтобы сделать этот пример проще, я создам приложение, в котором динамически будет применен скин к кнопке. Приложение получит SWF со скинами в runtime, загрузит скины и применит их к кнопке. Я буду использовать файл со скинами из статьи про скинование от NJ (на русском) и применять скины RadioButton к виджету Button. (Спасибо Roger Gonzalez и NJ за это решение.)
Наборы скинов – это вышеупомянутый файл с темплейтом. Я хочу создать SWF оболочку, которую мое приложение сможет загрузить в runtime и извлечь необходимые скины, в данном случае это четыре символа для виджета RadioButton. Вот исходный код SWF оболочки:
package
{
import flash.display.Sprite;
public class Wrapper extends Sprite
{
[Embed(source="flex_skins.swf",symbol="RadioButton_upIcon")]
public var rbUpSkin: Class;
[Embed(source="flex_skins.swf",symbol="RadioButton_downIcon")]
public var rbDownSkin: Class;
[Embed(source="flex_skins.swf",symbol="RadioButton_disabledIcon")]
public var rbDisabledSkin: Class;
[Embed(source="flex_skins.swf",symbol="RadioButton_overIcon")]
public var rbOverSkin: Class;
}
}
Flex приложение должно загружать файл откуда угодно!
Я создал класс-утилиту ClassLoader для загрузки SWF и извлечения класса. Вот основной код:
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
...
request = new URLRequest(swfLib);
var context:LoaderContext = new LoaderContext();
context.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
loader.load(request, context);
Обратите внимание, что Loader загружает SWF в ApplicationDomain, который является дочерним к текущему домену. Таким образом мы убеждаемся, что приложение может получить доступ к загруженному классу и его наборам.
Загружаем класс (в данном случае, "Wrapper"):
var wrapperClass:Class = loader.contentLoaderInfo.applicationDomain.getDefinition(className) as Class;
var wrapper:Object = new wrapperClass();
Теперь вам необходимо применить скины к кнопке:
StyleManager.getStyleDeclaration("Button").setStyle("upSkin", wrapper.rbUpSkin);
StyleManager.getStyleDeclaration("Button").setStyle("downSkin", wrapper.rbDownSkin);
StyleManager.getStyleDeclaration("Button").setStyle("disabledSkin", wrapper.rbDisabledSkin);
StyleManager.getStyleDeclaration("Button").setStyle("overSkin", wrapper.rbOverSkin);
Приложение (смотреть здесь, включена возможность просмотра исходников) отображает текстовое поле, в которое вы можете ввести адрес SWF оболочки. (например, этой). Затем нажмите "Apply" и скин кнопки изменится. При наведении или клике мыши скин изменяется на соответствующий.
Что же с этим делать? Динамическое скинование дает нам огромную выгоду: вы даете возможность пользователям использовать свои скины в ваших приложениях. Представьте Winamp на Flex. Разработчику не надо создавать библиотеку скинов, пользователь сам сможет загрузить скины откуда-нибудь из внешних библиотек скинов. Также хорошо то, что разработчик может контролировать, какие виджеты могут сменить скин, а какие должны остаться не тронутыми (вызов setStyle только для нужных виджетов). И, наконец, внешнее хранение файлов со скинами обеспечит меньший вес файла приложения.