Константин Ковалев предложил более простой и изящный вариант решения проблемы затронутой мною в предыдущем посте.
Суть решения заключается в использовании метатега RemoteClass в классе сохраняемого объекта. Т.е registerClassAlias не требуется. Код ниже:
XML:
<mx:Application | |
creationComplete="onCreationComplete()" | |
xmlns:mx="http://www.adobe.com/2006/mxml" | |
layout="vertical"> | |
<mx:Script> | |
<![CDATA[ | |
import com.riapriority.tests.DataObject; | |
| |
private var so:SharedObject; | |
private var dataObject:DataObject; | |
| |
| |
private function onCreationComplete(): void | |
{ | |
dataObject = new DataObject(); | |
so = SharedObject.getLocal ("test"); | |
} | |
| |
| |
private function dataObjectGeneration (): void | |
{ | |
dataObject.generate(); | |
ta.text += 'GENERATING: ' + dataObject.toString() + '\n'; | |
} | |
| |
private function saveLocalData(): void | |
{ | |
so.data.DataObject = dataObject; | |
so.flush(); | |
ta.text += 'SAVE: ' + dataObject.toString() + '\n'; | |
} | |
| |
private function loadLocalData(): void | |
{ | |
so = SharedObject.getLocal('test'); | |
if(so.data.DataObject != null) | |
{ | |
dataObject = so.data.DataObject as DataObject; | |
ta.text += 'LOAD: ' + dataObject.toString() + '\n'; | |
} | |
else | |
{ | |
ta.text += 'LOAD: null\n'; | |
} | |
} | |
]]> | |
</mx:Script> | |
| |
<mx:Button | |
label="Generate" | |
click="{dataObjectGeneration()}"/> | |
<mx:Button | |
label="Save" | |
click="{saveLocalData()}"/> | |
<mx:Button | |
label="Load" | |
click="{loadLocalData()}"/> | |
<mx:TextArea | |
width="50%" | |
height="50%" | |
id="ta"/> | |
</mx:Application> |
и код класса
- package com.riapriority.tests
- {
- import mx.formatters.DateFormatter;
- [Bindable]
- [RemoteClass(alias="com.riapriority.tests.DataObject")]
- public class DataObject
- {
- public var date: Date;
- public var random: String;
- public function generate(): void
- {
- date = new Date();
- random = "" + Math.random() * 10000;
- }
- public function toString():String
- {
- var df:DateFormatter = new DateFormatter ();
- df.formatString = "JJ:NN:SS DD/MM/YYYY";
- return "\n date: " + df.format(date) + "\n random: " + random;
- }
- }
- }
[Bindable] здесь не обязателен, но как тонко выразился Константин,
класс без [Bindable], что штопор без бутылки ![]()
Upd: Работает только с публичными свойствами (касается так же вложенных объектов). Вложенные объекты так же должны быть снабжены метатегом RemoteClass.
Продолжу тему затронутую Александром Гаховым в статье Клонирование объектов утилитой ObjectUtil.copy.
Здесь я приведу пример использования registerClassAlias для восстановления объектов определенного класса из SharedObject. Суть проблемы заключалась в том, что помещенные в SharedObject экземпляры определенного класса при перезапуске возвращались, как экземпляры класса Object. Когда я столкнулась с этой проблемой, доступного для понимания примера мне нагуглить не удалось. Перед размещением же поста, я нашла вопрос по этой теме в ruFlex, однако приведенный там пример показался мне не вполне наглядным. Предлагаю вашему вниманию свой пример решения этой проблемы:
XML:
<mx:Application creationComplete="onCreationComplete()" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> | |
<mx:Script> | |
<![CDATA[ | |
import src.DataObject; | |
import flash.net.*; | |
| |
private var so: SharedObject; | |
private var dataObject: DataObject; | |
| |
| |
private function onCreationComplete(): void | |
{ | |
dataObject=new DataObject(); | |
registerClassAlias('src.DataObject', DataObject); | |
so = SharedObject.getLocal("test"); | |
} | |
| |
| |
private function dataObjectGeneration(): void | |
{ | |
dataObject.generate(); | |
ta.htmlText+='GENERATION: '+dataObject.toString()+'\n'; | |
} | |
| |
private function saveLocalData(): void | |
{ | |
so.data.DataObject=dataObject; | |
so.flush(); | |
ta.htmlText+='SAVE: '+dataObject.toString()+'\n'; | |
} | |
| |
| |
| |
private function loadLocalData(): void | |
{ | |
so = SharedObject.getLocal('test'); | |
if(so.data.DataObject!=null) | |
{ | |
dataObject=so.data.DataObject as DataObject; | |
ta.htmlText+='LOAD: '+dataObject.toString()+'\n'; | |
} else { | |
ta.htmlText+='LOAD: null\n'; | |
} | |
} | |
| |
]]> | |
</mx:Script> | |
<mx:Button x="193" y="27" label="save" id="save_" click="{saveLocalData()}"/> | |
<mx:Button x="107" y="27" label="generate" id="generate" click="{dataObjectGeneration()}"/> | |
<mx:Button x="49" y="27" label="load" id="load_" click="{loadLocalData()}"/> | |
<mx:TextArea x="49" y="57" height="141" width="618" id="ta"/> | |
</mx:Application> |
Собственно сам класс нашего объекта.
- ackage src
- {
- import mx.formatters.DateFormatter;
- public class DataObject
- {
- public var date: Date;
- public var random: String;
- public function DataObject()
- {
- }
- public function generate(): void
- {
- date=new Date();
- random=""+Math.random()*10000;
- }
- public function toString():String
- {
- var df: DateFormatter=new DateFormatter();
- df.formatString="JJ:NN:SS DD/MM/YYYY";
- return "\n date: "+df.format(date)+"\n random: "+random;
- }
- }
- }
Прошу прощения за подобное отображение mxml-ины. Если пример вас заинтересует скопируйте его. ![]()
Путь скинизации посредством css тернист и многотруден. Особенно если каждый стиль требуют собственных скинов для виджетов (иконок, кнопок и т.д). Предположим нам надо подгрузить картинку. Для примера создадим класс наследованный от UIComponent и требующий подгрузки картинки описанной в css. Назовем класс Example.as.
Стили для него описаны в одноименном блоке стиля. (вы разумеется знаете, что стили цепляются к одноименной компоненте автоматом и не требуют каких-то дополнительных директив?)
Казалось бы чего проще?…
XML:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="src.*"> | |
<!−− так конечно делать не надо, но для примера покатит :) с css будет так же!−−> | |
<mx:Style> | |
Example{image: Embed("graphics/image.png");} | |
</mx:Style> | |
<ns1:Example x="42" y="24"/> | |
</ns1:Example></mx:Application> |
- package src
- {
- import mx.core.UIComponent;
- import mx.controls.Image;
- public class Example extends UIComponent
- {
- private var image: Image;
- public function Example()
- {
- }
- override protected function createChildren(): void
- {
- super.createChildren();
- image=new Image();
- image.source=getStyle("image");
- addChild(image);
- }
- }
- }
И после компиляции картинку благополучно не видим. Почему? Потому, что по умолчанию ее размеры составляют 0 x 0. Для того чтобы она отобразилась корректно требуется задать ей реальные размеры, каковые нам любезно предоставляют свойства measuredWidth и measuredHeight. Однако актуальные значения ширины и высоты будут доступны только после вызова метода protected function measure(): void UIComponent, о котором доступна подробная информация в хелпе.
Ну и собственно результат:
- package src
- {
- import mx.core.UIComponent;
- import mx.controls.Image;
- public class Example extends UIComponent
- {
- private var image: Image;
- public function Example()
- {
- }
- override protected function createChildren(): void
- {
- super.createChildren();
- image=new Image();
- image.source=getStyle("image");
- addChild(image);
- }
- override protected function measure():void
- {
- super.measure();
- image.setActualSize(image.measuredWidth, image.measuredHeight);
- }
- }
Следует отметить, что подобных проблем с размерами не возникает в mxml-e.
Сегодня у Константина Палыча день рождения!
Счастье у тебя уже есть, поэтому дальнейших творческих узбеков тебе, Костик, и чтобы твое здоровье тебе ни в чем не отказывало! ![]()

Счастья, здоровья, хорошего аппетита, трудовых свершений, а главное, крепких нервов в совместной работе с твоей горячо любимой мной ![]()
При большом количестве меток (да и вообще компонент) и частой их отрисовке вы непременно столкнетесь с тормозами. В случае с метками есть альтернатива в лице mx.core.UITextField, которой и посвящен этот пост. Что нас привлекает в mx.controls.Label? Возможность отобразить некий текст и применить к нему стили. UITextField нам эти возможности предоставляет так же. Однако есть одна тонкость. Как работать со стилями если:
public function setStyle(styleProp:String, value:*):void - Does nothing. A UITextField cannot have inline styles.
Не очень понятно зачем вообще нужен этот метод если он ничего не делает. Была мысль, что он описан в каком-то из имплементящихся интерфейсов, но подтверждения этому в коде найти не удалось. Впрочем я отвлеклась. Ну так как же работать со стилями? Да в общем точно так же, только через свойство styleName.
styleName:Object [read-write] - The class style used by this component. This can be a String, CSSStyleDeclaration or an IStyleClient.
Т.е uiTextField.styleName.setStyle("color", 0xff0000); сработает, но только если к UITextField уже был применен какой-то стиль. Не поленилась написать небольшой пример:
XML:
<?xml version="1.0" encoding="utf-8"?> | |
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> | |
<mx:Script> | |
<![CDATA[ | |
import mx.core.UITextField; | |
import mx.styles.*; | |
| |
private var uiTextField: UITextField; | |
| |
override protected function createChildren(): void | |
{ | |
super.createChildren(); | |
uiTextField=new UITextField(); | |
addChild(uiTextField); | |
uiTextField.text="mx.core.UITextField"; | |
trace("uiTextField.styleName: "+uiTextField.styleName); | |
// uiTextField.styleName: null | |
var styleCss:CSSStyleDeclaration = new CSSStyleDeclaration(); | |
styleCss.setStyle('fontSize', 15); | |
styleCss.setStyle('color', picker.selectedColor); | |
uiTextField.styleName=styleCss; | |
} | |
| |
public function changeColor(): void | |
{ | |
uiTextField.styleName.setStyle("color", picker.selectedColor); | |
} | |
| |
]]> | |
</mx:Script> | |
<mx:ColorPicker id="picker" change="changeColor()" /> | |
</mx:Application> |
При работе с чартингом, одной из осей которого является DateTimeAxis, часто возникает потребность в изменении формата отображения меток. Как я поняла в ходе общения с кодом DateTimeAxis, сам флекс не меняет формат даты в зависимости от локали. По умолчания дата отображается в формате m/d/yy. Для российского глаза такое отображение несколько непривычно. Хелп предоставляет два пути решения этой проблемы.
1. labelFunction:Function [read-write]
Called to format axis values for display as labels. A labelFunction has the following signature:
- function labelFunction(labelValue:Object, previousValue:Object, axis:IAxis):String
If you know the types of the data your function will be formatting, you can specify an explicit type for the labelValue and previousValue parameters.
Казалось бы вот оно счастье, однако поспешность в выборе может послужить для вас причиной дополнительного геморроя. Качественная замена функции дело довольно муторное. Ведь интервал между метками может составлять как секунды так и годы. Куча кода. Рекомендую с негодованием отвергнуть эту веселую перспективу и обратить свой взор к пункту 2.
2. В хелпе есть такое понятие как Protected Methods. Для человека пришедшего из AS2 и не озаботившегося ознакомлением с матчастью AS3 такое буквосочетание может показаться слабо информативным, однако белые люди сжевавшие в процессе общения с флексом уже не одну pdf-ную страницу в курсе что Protected Methods - методы класса которые можно переопределять в наследниках. Часто большинство этих методов скрыто в недрах хелпа в аккордеоне с надписью “Show Inherited Protected Methods", однако это не наш случай. Интересующие нас сейчас методы DateTimeAxis на виду:
- formatDays(d:Date, previousValue:Date, axis:DateTimeAxis):String
- formatMilliseconds(d:Date, previousValue:Date, axis:DateTimeAxis):String
- formatMinutes(d:Date, previousValue:Date, axis:DateTimeAxis):String
- formatMonths(d:Date, previousValue:Date, axis:DateTimeAxis):String
- formatSeconds(d:Date, previousValue:Date, axis:DateTimeAxis):String
- formatYears(d:Date, previousValue:Date, axis:DateTimeAxis):String
Разумеется прежде чем переопределять их в соответствии с вашими пожеланиями следует ознакомиться с исходным кодом DateTimeAxis. Там за вас уже все придумали и написали:
- protected function formatYears(d:Date, previousValue:Date, axis:DateTimeAxis):String
- {
- var fy:Number = d[fullYearP];
- return fy.toString();
- }
- protected function formatMonths(d:Date, previousValue:Date, axis:DateTimeAxis):String
- {
- var fy:Number = d[fullYearP];
- return (d[monthP] + 1) + "/" + ((fy % 100) < 10 ? "0" + fy % 100 : fy % 100);
- }
- protected function formatDays(d:Date, previousValue:Date, axis:DateTimeAxis):String
- {
- var fy:Number = d[fullYearP];
- return (d[monthP] + 1) + "/" + d[dateP] + "/" + ((fy % 100) < 10 ? "0" + fy % 100 : fy % 100);
- }
- protected function formatMinutes(d:Date, previousValue:Date, axis:DateTimeAxis):String
- {
- return d[hoursP] + ":" + (d[minutesP] < 10 ? "0" + d[minutesP] : d[minutesP]);
- }
- protected function formatSeconds(d:Date, previousValue:Date, axis:DateTimeAxis):String
- {
- return d[hoursP]+ ":" + (d[minutesP] < 10 ? "0" + d[minutesP] : d[minutesP]) + ":" + (d[secondsP] < 10 ? "0" + d[secondsP] : d[secondsP]);
- }
Потребуется только слегка откорректировать эти методы в соответствии с вашими пожеланиями. Или, если приложение требует некоторой гибкости, добавить к уже имеющемуся формату еще несколько форматов отображения. (в зависимости от локали)
Кстати. Если вдруг получившиеся в ходе переопределения методов метки будут натыкаться друг на друга и делать подписи абсолютно нечитаемыми следует воспользоваться методом reduceLabels(intervalStart:AxisLabel, intervalEnd:AxisLabel):AxisLabelSet, а никак не свойством interval : Number, как это может захотеться с первого взгляда. Но это уже совсем другая история…
10> 10> 10>
Если по какой-то нелепой случайности кто-то из вас еще не ознакомился с этим волшебным сборником советов, настоятельно рекомендую исправить это досадное недоразумение.
ЗЫ: Уже на русском. За что огромная благодарность команде переводчиков!
