Танцы с бубном, описанные в прошлом посте не актуальны для SDK 3.5 и выше ![]()
В ходе работы с пиккером, мы столкнулись с его неприятной особенностью: при попытке заменить предустановленный цвет пиккера на черный (самый первый элемент таблицы пиккера) контролл аккуратно закрывался, но и только. Т.е событие change не диспатчилось и текущий цвет не менялся.
В чем же проблема?
У стандартного пиккера предустановлена коллекция отображаемых в таблице цветов. Она передается в дата провайдер и содержит в себе 240 цветовых квадратов. Отображаемые в таблице цвета можно регулировать путем замены или модификации этой коллекции.
Для управления выбранным цветом контролла предусмотрено 2 свойства: selectedIndex selectedColor. Очевидно что selectedIndex — индекс цвета в коллекции дата провайдера. А вот с selectedColor все не так однозначно. При всем изобилии, таблица пиккера не вмещает в себя все могущие прийти в голову дизайнера цвета. И, когда вы пытаетесь в качестве selectedColor указатиь пиккеру значение отсутствующее в коллекции дата провайдера, происходит следующее.
* selectedColor устанавливается в заданный цвет
* В дата провайдере осуществляется поиск индекса итема содержащего этот цвет.
* Если такой итем найден, в selectedIndex прописывается соответствующее значение.
* Если же в дата провайдере отсутствует данный цвет, selectedIndex не изменяется.
Следует отметить, что по умолчанию selectedIndex и selectedColor выставлены в 0. Т.е когда мы пытаемся установить selectedColor в цвет отсутствующий в коллекции, selectedIndex не меняется и остается равным 0. В момент же интерактивного выбора нужного цвета в пиккере на каком-то из этапов осуществляется проверка соответствия индекса выбираемого значения текущему. Если значения совпадают, ничего не происходит. Отсюда и этот прекрасный баг. В selectedIndex-е пиккера прямо указано, что цвет у нас черный. А вот значение selectedColor при этом совершенно никого не волнует
Что делать?
Решение очевидно. Надо добавить в коллекцию дата провайдера недостающий цвет.
Вот так выглядит стандартный пиккер

Так он будет выглядеть если добавить новый цвет в конец коллекции.

Не слишком симпатично. Поэтому я предлагаю обратить внимание на второй столбец цветовой палитры. Он совершенно черный. Нехитрые исследования предустановленной коллекции дата провайдера показали, что перед нами столбец нулевых значений.
Для чего он, мне понять не удалось. А раз так, ничто не мешает добавлять в него новые цвета.
Нулевые элементы палитры начинаются с 1 индекса и повторяются через каждые 20, вплоть до 221 индекса.
Все что нужно это:
* Проверить значение selectedIndex после установки selectedColor.
* Если индекс не изменился, ищем первый нулевой элемент коллекции и присваиваем ему значение нового цвета.
* Если нулевые значения кончились добавляем новый цвет в конец коллекции.
Ниже код и превью результата
Code:
package | |
{ | |
import mx.collections.ListCollectionView; | |
import mx.controls.ColorPicker; | |
| |
public class ChartColorPicker extends ColorPicker | |
{ | |
public function ChartColorPicker() | |
{ | |
super(); | |
} | |
| |
override public function set selectedColor(value:uint):void | |
{ | |
| |
if(dp && !dp.contains(value)) | |
{ | |
var ind: int = findFirstEmptyItemIndex(); | |
if(ind == -1) dp.addItem(value); | |
else dp.setItemAt(value, ind); | |
} | |
super.selectedColor = value; | |
} | |
| |
private function get dp(): ListCollectionView | |
{ | |
if( dataProvider is ListCollectionView) return dataProvider as ListCollectionView; | |
else return null; | |
} | |
| |
private const FIRST_EMPTY_INDEX: int = 1; | |
private const EMPTY_INDEX_SHIFT: int = 20; | |
| |
private function findFirstEmptyItemIndex(): int | |
{ | |
if(!dp) return -1; | |
| |
for(var i: int = FIRST_EMPTY_INDEX; i<dp.length; i+=EMPTY_INDEX_SHIFT) | |
{ | |
if(dp.getItemAt(i) == 0) | |
{ | |
return i; | |
} | |
} | |
return -1; | |
} | |
} | |
} |

Константин Ковалев предложил более простой и изящный вариант решения проблемы затронутой мною в предыдущем посте.
Суть решения заключается в использовании метатега 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.
Сегодня у Константина Палыча день рождения!
Счастье у тебя уже есть, поэтому дальнейших творческих узбеков тебе, Костик, и чтобы твое здоровье тебе ни в чем не отказывало! ![]()
По ряду причин была вынуждена установить на свою рабочую машину Vista. Расскажу о тех проблемах, с которыми столкнулась в ходе установки flex builder2 под Vista. Сразу оговорюсь переходить на 3-й builder мы не стали т.к он бета и показался нам не стабильным.
Проблема раз. Попытка установить второй билдер под eclipse 3.3:

Проблема два. Вернее легкое неудобство. В процессе установки инсталятор отказался демонстрировать контролы. Просто не отображал. Устанавливать пришлось на автопилоте руководствуясь подсознанием. Установила. Честно признаюсь не с первого раза.
Update установила.
Проблема три. SVN отказался дружить с подцепленым проектом. Проблема довольно специфическая, врядли у кого-то повторится, поэтому подробно останавливаться на ней не буду. Починила.
Установила subclips.
Последняя капля. Эклипс периодически вылетал с предупреждением следующего вида

В debugg-режиме с прерываниями он вылетал постоянно. Переставляла builder и java. Не помогло.

Счастья, здоровья, хорошего аппетита, трудовых свершений, а главное, крепких нервов в совместной работе с твоей горячо любимой мной ![]()
На протяжении довольно долгого времени я пользовалась OmeaReader-ом. Была я вполне счастлива и довольна, пока в один прекрасный день omea не задергался в конвульсиях и не умер прихватив с собой всю мою базу. Довольно неприятное обстоятельство, особенно учестывая, что легко восстановить мне ее не удалось. В начале не помогла перезагрузка, а потом и переустановка. Спас почищенный реестр, но рисковать так более я готова не была. Возникла потребность в поиске другого RSS-reader-а, результатом чего и стал этот пост. Обзоров было уже довольно много. И зачастую они весьма противоречивы. Я же только поделюсь с вами своим абсолютно субъективным мнением.
Mozilla Thunderbird. 2.0.0.6
Клиент бесплатный. Mozilla Firefox был и остается моим самым любимым браузером. Разумеется на Thunderbird я обратила свое пристальное внимание и… И мои надежды не оправдались. Как rss-клиент мне он показался глючным, неудобным и, что самое неприятное, он теряет каналы. Потерянные каналы не отображаются в списке подписок, но Thunderbird свято верит, что они где-то есть и при попытке повторной подписи выкидывает эксепшен. Потерянных каналов было только 2, но мне как-то хватило. Кроме того, порадовала приятная необходимость всякий раз при запуске подтверждать пароли на все каналы требующие аутентификации. Подтверждать нажатием кнопочки enter. Однако, если таковых каналов у вас не один, мягко говоря, это начинает нервировать. Отключить этот бонус мне не удалось.
И тем не менее пока Thunderbird остался моим основным почтовым клиентом.
ЗЫ: В ходе изысканий был найден, приятный, на мой взгляд, плагин сворачивающий Firefox и Thunderbird в трей
Microsoft Office Outlook 2007
Буду краткой. Не халява. Запутанный и перегруженный интерфейс. Слишком умный для меня. Мой моск, в плане продвинутости, не готов с ним конкурировать. Не нравится.
Abilon 2.5.3 b 196
Бесплатный. Легкий и быстрый. Вполне удобный. Все бы с ним хорошо, если бы не одна небольшая фигня. Отказался правильно парсить жж-шные посты. ![]()
FeedDemon 2.3.0.10
Не халява. Но выбрала я именно его. Боюсь даже, что он затмил в моих глазах OmeaReader в его неглючные времена. Он нравится мне всем, кроме стоимости.
Не буду вдаваться в пространное описание тем более что таковых в сети и без моего предостаточно. Заинтересованные могут почитать о нем например тут и не только о нем тут
:: Следующие >>
