Всякие разные штуки сомнительной полезности сделанные в свободное от работы время.
Пользовательские мета-теги.
Во-первых, спасибо Philippe с http://www.flashdevelop.org/ за наводку.
Во-вторых, конечно, если вы и раньше об этом знали, вам будет не интересно.
Оказывается, используя Flex SDK можно определить пользовательские мета-теги. Вам наверняка знакомы [Embed], [Bindable], [Event] и еще мног разных документированых и не очень тегов. А сейчас мы попробуем разобраться, как создать свой тег и как его потом использовать.
Цель проекта: мне очень нравится возможность использования стилей во Флексовых приложениях, но необходимость ради этого импортировать менеджер стилей и все сопутствующие классы удручает Да и вообще, зачем пользоваться чем-то готовым, если можно написать по-своему, изобрести велосипед в который раз и получить удовольствие от процесса?
В аттачменте готовый FD проект *.
Для начала скомпилируем SWC, принципиальным является указать -keep-as3-metadata=<имя метатега> в дополнительных аргументах компилятора.
Вот мои настройки, вам нужно будет изменить пути так, чтобы они соответствовали вашему проекту:
Билд-файл:
Код:
<?xml version="1.0" encoding="utf-8"?> <flex-config> <compiler> <source-path append="true"> <path-element>C:\www\projects\csstest\SWC-sources</path-element> <path-element>C:\Program Files\FlashDevelop\Library\AS3\classes</path-element> </source-path> </compiler> <include-classes> <class>ICSSClient</class> </include-classes> </flex-config>
Код:
set sdk=C:\Flex_sdk_3_3\bin\compc.exe set buildfile=C:\www\projects\csstest\run\buildSWC.xml set outputfile=C:\www\projects\csstest\lib\lib.swc set exec=%sdk% -load-config+=%buildfile% -debug=true -incremental=true -benchmark=false -output=%outputfile% -strict=true -keep-as3-metadata=CSS call %exec% pause
Подключаем скомпилированый SWC к проекту - все, можно использовать наш тег
Следующим шагом создадим класс, который будет этот тег использовать:
package { //{imports import flash.display.Sprite; import flash.events.Event; import flash.geom.Rectangle; import flash.utils.getQualifiedClassName; //} // See the rectangular shape drawn in different coordinates // with different dimensions. [CSS(bounds="0,0,100,100")] // Violet, see it is set to yellow once we apply styling. [CSS(color="#FF00FF")] public class CSSSprite extends Sprite implements ICSSClient { private var _bounds:String; [Bindable("boundsChange")] /** * ... * This property can be used as the source for data binding. * When this property is modified, it dispatches the <code>boundsChange</code> event. */ public function get bounds():String { return _bounds; } public function set bounds(value:String):void { if (_bounds == value) return; _bounds = value; var temp:Array = value.split(/\s*,\s*/); boundsRect = new Rectangle(Number(temp[0]), Number(temp[1]), Number(temp[2]), Number(temp[3])); dispatchEvent(new Event("boundsChange")); // You usually would like to deffer draw() until // the next screen update, but we don't want to // overcomplicate this example. draw(); } private var _color:String; [Bindable("colorChange")] /** * ... * This property can be used as the source for data binding. * When this property is modified, it dispatches the <code>colorChange</code> event. */ public function get color():String { return _color; } public function set color(value:String):void { if (_color == value) return; _color = value; colorStyle = parseInt(value.match(/[\dABCDEF]+/ig)[0], 16); dispatchEvent(new Event("colorChange")); draw(); } protected var boundsRect:Rectangle; protected var colorStyle:uint; public function CSSSprite() { super(); CSSManager.styleObject(this); } /* INTERFACE ICSSClient */ public function get className():String { var temp:String = getQualifiedClassName(this); if (temp.indexOf(":") < 0) return temp; return temp.replace(/.*([^:]+$)/, "$1"); } public function draw():void { // we don't set initializes, though, usually you'd like to have those // so you could draw as soon as the object is created. if (!boundsRect) return; graphics.clear(); graphics.beginFill(colorStyle); graphics.drawRect(boundsRect.x, boundsRect.y, boundsRect.width, boundsRect.height); graphics.endFill(); } } }
package { //{imports import flash.utils.describeType; //} public class CSSManager { // Just some test CSS declaration. You will probably want to expand that // to be embedded or loaded at runtime. private static var _styles:XML = <![CDATA[ AnotherStyle { foo: bar; } CSSSprite { bounds: 50, 100, 150, 150; color: #FFFF00; some-unrelated-property: whatever } YetAnotherStyle {} ]]>; public function CSSManager() { super(); } public static function styleObject(object:ICSSClient):void { var className:String = object.className; // We simplify CSS parsing as much as possible. // You will probably want to implement // your own parsing routine. var declarations:String = _styles.toString().replace(/[\r\n\t]*/g, ""); var declarationRE:RegExp = new RegExp(className + "\\s*\\\{[^\\\}]*\\\}", "g"); var declaration:String = declarations.match(declarationRE)[0]; declaration = declaration.match(/([^\}\{]*)\}/)[1]; var props:Object = { }; var propsDeclarations:Array = declaration.split(";"); var prop:String; var val:String; var pair:Array; // Here we look for the metadata declarations stored with our class. var applicableStyles:XMLList = describeType(object).metadata.(@name == "CSS").arg; var keys:Array = []; applicableStyles.@key.(keys.push(toXMLString())); var p:String for each(p in propsDeclarations) { pair = p.split(":"); prop = pair[0]; val = pair[1]; // Filter properties that aren't related to our style client. // We could also check here for default values, // e.g. if the value in <code>@value</code> attribute is the same as the one // we want to assign, we may also skip that style property. if (keys.indexOf(prop) < 0) continue; props[prop] = val; } for (p in props) { // Here you want to parse values from string and convert them // To the appropriate types. // But for the sake of simplicity we will assigns // strings and handle the parsing on the ICSSClient's side. object[p] = props[p]; } } } }
Попробую более подробно объяснить. На самом деле это не сложно, я, наверное просто очень запутано изложил.
Для начала, исходная информация:
http://www.adobe.com/cfusion/communi...2&postId=11907
То, что я сделал, это пример практического применения.
Задача:
Создать свой мета-тег [CSS], который мы впоследстии сможем использовать в нашем AS3 или MXML проекте для того, чтобы задавать стили нашим визуальным компонентам. Что-то похожее на уже существующий тег [Style] только очень упрощенный.
Использовать мы его будем так:
- [CSS(propertyName="defaultValue")]
- для этого нам обязательно в классе применяющем этот тег нужно будет объявить свойство с именем propertyName типа String (это в нашем примере, но вы наверняка захотите это переделать по-своему, чтобы парсить стили в менеджере, а в классе использующем стиль задавать свойства не в виде строки, а уже в виде правильного типа).
Т.е. нам нужно будет такое свойство:
protected var _propertyName:Type; public function set propertyName(value:String):void { _propertyName = valueFromString(value, Type) as Type; } protected function valueFromString(value:String, type:Class):Object { switch (type) { case Number: return parseFloat(value); case Type: return new Type(value); // Предположим, что объекты типа Type создаются именно таким образом. .... // наверняка разных типов будет больше. } return null; }
в этом методе CSSManager прочитает метаданные доступные для этого объекта, потом прочитает CSS стили, найдет стиль соотвествующий theClient и задаст propertyName свойству значение из стиля.
Решение:
- Чтобы наши мета-теги вообще записались в декларацию класса нам нужно будет скомпилировать SWC с параметром -keep-as3-metadata=CSS. Потом подключить этот CSS к проекту и использовать какой-нибудь класс или интерфейс из этого SWC. (Не объязательно использовать мета-тег в классе, который компилируется в SWC, даже, скорее, не нужно этого делать т.как тестировать будет неудобно).
Для этого мы компилируем интерфейс ICSSClient в SWC, подключаем SWC к проекту и имплементим этот интерфейс в CSSSprite. (Не оставляйте ICSSClient.as в зоне видимости компилятора т.как AS файлы имеют приоритет при компиляции, т.е. если вы оставите этот интерфейс на classpath, то скомпилируется именно он, а не тот, что в SWC).
- Создаем класс CSSSprite который использует тег [CSS].
- Создаем класс CSSManager который парсит CSS и мета данные, а так же инициализирует свойства связаные со стилями.
Мета-данные мы можем получить распарсив XML возвращаемый describeType(). Например, данные по классу будут храниться в описании класса:
Код:
<factory type="CSSSprite"> <metadata name="CSS"> <arg key="propertyName" value="defaultValue"/> </metadata> .... </factory>
Дальше применив RegExp и E4X мы найдем какие же свойства нам нужно инициализировать, и какие значения им нужно присвоить.
Инициализируем свойства CSSSprite'а значениями получеными из CSS.
Всего комментариев 10
Комментарии
03.05.2009 03:08 | |
Цитата:
Для начала скомпилируем SWC, принципиальным является указать -keep-as3-metadata=<имя метатега> в дополнительных аргументах компилятора.
|
03.05.2009 05:19 | |
03.05.2009 15:10 | |
wvxvw, перечитал три раза и понял, что тема интересная))) Не мог бы ты написать об этом же, но понятно? Я конечно понимаю, что "понятно" - понятие субъективное, но это похоже на конспект гения, найденный наследником-балбесом... Я чувствую, что тут пахнет золотом, но разобраться одновременно в двух лабиринтах - твоей мысли и твоего кода - для меня мучительно трудно. Нельзя ли слегка расширить этот текст по высоте, объяснив - задачу, которую ты решаешь, метод ее решения по шагам(чтобы решить нашу задачу, нам надо сначала... и т.д.), и результат (это можно вкратце и с восторгом)). Был бы очень признателен, обожаю ФД и мне это все очень интересно))) Спасибо.
(ээ.. понимаешь, когда например смотришь чужой класс - скачанный как ресурс к статье, выложенный автором с превьюшкой - по крайней мере, имеешь представление о том, что он делает. а здесь надо разобрать твой код и понять, что же ты хотел сделать. Учитывая, что при этом ты рассказываешь о НОВОМ НЕИЗВЕДАННОМ способе это делать - задача становится нерешаемой. Может не только для меня.) |
|
Обновил(-а) Wolsh 03.05.2009 в 15:17
|
03.05.2009 17:15 | |
Написал дополнение. Возможно просто мой пример не самый удачный... надо было подумать о чем-то более наглядном...
|
03.05.2009 21:00 | |
О, теперь многое стало понятней! Спасибо, удачи!
|
05.05.2009 07:54 | |
Wolsh, способ ещё в 2006 (или 2007-ом) году был описан
Используем на всю катушку уже давно. |
05.05.2009 14:36 | |
@ __etc:
Если чесно, то я не нашел ни одного описания на русском языке. Может плохо искал. А так вообще, я думаю, что способ уже был во втором Флексе. И вообще по метадате очень мало информации... Т.е. даже "документированые" теги документированы не полностью, так что, я думаю, не удивительно, что кто-то этого не знает Да, и еще, ты говорил, что используете внешний CSS. А как вы его загружаете: в рантайме, или эмбедите? Я тут думал для самообразования попробовать написать на Яве транскодер для стилей. Просто интересно, может кто-то уже это сделал |
|
Обновил(-а) wvxvw 05.05.2009 в 18:01
|
02.08.2009 13:20 | |
В рантайме.
|
Последние записи от wvxvw
- Dired - текстовый проводник по файловой системе (29.06.2013)
- Навигация по HTML с WASD (09.06.2012)
- JavaScript, все не так плохо (07.06.2012)
- Что такое tarball и чем его пакуют (11.04.2012)
- Критика Presentation Model (18.02.2012)