Posts Tagged ‘ ActionScript

Fabrication Framework 0.8.7 released!!!

Mój blog od bardzo długiego czasu pozostawał bez nowych treści. Nadszedł jednak czas, chęci i okazja ku temu by to zmienić.

Jestem jednym z Commiter-ów frameworka Fabrication wykorzystywanego do budowy aplikacji Flash/Flex. Zachęcony dynamicznym rozwojem Flex-a pod skrzydłami Apache Teamu postanowiłem wydać wersję frameworka w której będą używane tylko sparkowe komponenty Flex-a.

Oto co zmieniłem:

Fabrication www: Fabricaiton Framework

Dostęp do itemrenderer-ów w Spark DataGroup

Czwarta odsłona Flex SDK przyniosła ze sobą nowe rodzaje kontenerów ułatwiających rozmieszczanie komponentów w aplikacjach flex-owych. Jednym z nich jest DataGroup. Nie chcę tutaj opisywać możliwości kontenera tylko przedstawić sposób w jaki uzyskałem dostęp do utworzonych w moim DataGroup itemrenderer-ów.

Mój itemrenderer wyświetla imię i nazwisko na podstawie danych zawartych w obiekcie "PersonVo".

Klasa PersonVo.

public dynamic class PersonVo
{
		public var firstName:String;
		public var surname:String;

		public function PersonVo(firstName:String = null,
					surname:String = null)
		{
			this.firstName = firstName;
			this.surname =surname;
		}
}

Kod itemrenderer-a

<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:s="library://ns.adobe.com/flex/spark"
		xmlns:mx="library://ns.adobe.com/flex/mx"
		autoDrawBackground="true">
	<s:HGroup gap="5"
			  height="100%">
		<s:Label id="firstName"
			 text="{data.firstName}"/>
		<s:Label id="surname"
			 text="{data.surname}"/>
	</s:HGroup>
</s:ItemRenderer>

Dostęp do każdego utworzonego obiektu itemrenderera uzyskuję rejestrując metodę dla zdarzenia "rendererAdd". W jej argumencie zostaje dostarczony obiekt "event" typu "RendererExistenceEvent", który to zawiera instancję itemrenderera. Wszystkie obiekty itemrenderer-ów umieszczam w tablicy i udostępniam na zewnątrz kontenera DataGroup poprzez publiczną właściwość.

DataGroup z przechowanymi obiektami itemrenderer-ów.

<s:DataGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
	        xmlns:s="library://ns.adobe.com/flex/spark"
	        xmlns:mx="library://ns.adobe.com/flex/mx"
                   itemRenderer="dgControls.DgControlRenderer"
	        rendererAdd="onRendererAdd(event)">
	<fx:Script>
		<![CDATA[
			import spark.events.RendererExistenceEvent;
			private var _itemRenderers:Array = [];

			/**
			 * Array of itemrenderers
			 * */
			public function get itemRenderers():Array
			{
				return _itemRenderers;
			}

			/**
			 * Add renderer to array
			 * */
			protected function onRendererAdd(event:RendererExistenceEvent):void
			{
				_itemRenderers.push(event.renderer);
			}
		]]>
	</fx:Script>
</s:DataGroup>

Do dataprovidera kontenera DataGroup dostaczyłem następującą tablicę oraz przeprowadziłem prostą iterację wraz ze zmianą właściwości wewnątrz itemrenderera. W zasadzie było to dość proste, ale czy to jedyne rozwiązanie? ;)

Tablica elementów "dataProvidera" w DataGroup.

var dpDataGroup:ArrayCollection = new ArrayCollection([
						new PersonVo("Piotr", "Zarzycki"),
						new PersonVo("Marian", "Zakrzewski"),
						new PersonVo("Damian", "Zarzycki")
					]);

Zmiana właściwości obiektów itemrenderer-ów.

var count:int = dg.itemRenderers.length;

for(var i:int = 0; i < count; i++)
{
					var dgRenderer:DgControlRenderer = dg.itemRenderers[i];
					dgRenderer.firstName.text = "Ola";
					dgRenderer.surname.text = "O.";
}

FlexPaginator Released! ;)

Chyba każdy programista zetknął się z problemem paginacji czy to na tworzonej przez siebie stronie internetowej, czy też(jak w moim przypadku) w aplikacji flex-owej.
W rozwiązaniach tworzonych w pracy zazwyczaj duże ilości danych prezentowałem za pomocą kontrolki DataGrid w połączeniu z paginatorem udostępnionym pod adresem – link. Jednak wciąż rosnąca ich ilość szybko sprawiła, że wygrzebana w czeluściach internetu kontrolka przestała spełniać oczekiwania. Postanowiłem zmierzyć się z paginacją i stworzyć samodzielnie komponent, który znacznie ułatwił by, a w szczególności przyśpieszył poruszanie się po tysiącach stron udostępnianych danych. Właściwie żeby być szczerym do rozpoczęcia pracy nad kontrolką zachęcił mnie Redliquid, a jej obecny wygląd-funkcjonalność jest w 100% jego pomysłem. :) Zaprezentowany flex-paginator jest zbudowany jeszcze na bazie kontrolek udostępnianych we Flex-ie 3 – planuję jednak jego dalszy rozwój, a więc poprawę wszelkich znalezionych błędów ;), a przy odrobinie większej ilości wolnego czasu przepisanie całości z wykorzystaniem dobrodziejstw Flex-a 4.

Diabeł tkwi w szczegółach, a więc poniżej prezentuję opis kilku najważniejszych opcji FlexPaginator-a.

Do pliku swc paginatora niezbędne jest dołączenie pliku css o nazwie StyleButtons.css, który odpowiada za wygląd wszystkich przycisków. Najważniejsze właściwości paginatora, które powinny zostać ustawione
w kodzie ActionScript lub po stronie mxml-a to:

  • totaItems – ilość wszystkich elementów
  • itemsPerPage – ilość elementów wyświetlanych na stronie
  • pageToDisplay – ilość wyświetlonych przycisków z numerami stron – ustawienie parzystej liczby wyświetlanych stron, skutkuję zamianę jej na nieparzystą ich ilość (takie „zachowanie” wynika z działania paginatora)

Opcjonalna jest natomiast właściwość:

  • selectedPage – numer zaznaczonej strony, po utworzeniu kontrolki domyślnie zostaje zanaczona środkowa strona/przycisk spośród aktualnie wyświetlonych.

Moja kontrolka paginacji zawiera przyciski umożliwiające odpowiednio:

  • skok o jedną stronę do przodu lub do tyłu
  • skok o wybraną ilość stron – wartości w wyświetlonym menu są wygenerowane automatycznie, ta para przycisków pojawia się tylko w przypadku gdy całkowita ilość stron przekracza 50.
  • skok na koniec i początek.

Dodatkowo obsługa zdarzenia „selectedPageChanged” daje możliwość odczytu aktualnie zaznaczonej strony. Wygenerowany paginator wyświetla nieparzystą liczbę stron,
a aktualnie zanaczony numer strony jest podkreślony

Przykładowy kod z zaznaczoną stroną nr 3 oraz plik css odpowiadający za poniższy wygląd paginatora pod linkiem

Wszelkiego rodzaju błędy, sugestię co mogę poprawić lub co zrobiłem tragicznie proszę zgłaszać na code-google (gdzie udostępnie kod projektu oraz plik swc – zakładka Download) lub w komentarzach pod tym czy też kolejnymi wpisami dotyczącymi FlexPaginator-a. ;)

DataGrid z pogrubioną zawartością wierszy

Podczas pracy nad pewną funkcjonalnością musiałem pogrubić zawartość wybranych wierszy w datagrid-zie. „Decyzja” o pogrubieniu zawartości danego wiersza miała być zawarta w każdym pojedyńczym obiekcie dostarczonym do datagrid-a w postaci tablicy (Array). Wewnątrz takiego obiektu zawarta zostanie właściwość „mówiąca” o pogrubieniu.

Przykładowa klasa zawierająca informację każdego wiersza w datagrid-zie mogła by wyglądać następnująco (właściwość „isSpecial” decyduję o pogrubieniu):

public dynamic class PersonVo
{
		public var name:String;
		public var surname:String;
		public var email:String;

  /**
		 * If this property is true, row in DataGrid is bold
		 *
		 * @var Boolean
		 * */
		public var isSpecial:Boolean;

		private var _date:Date;

		/**
		 * Get formatted date
		 *
		 * @retrun String
		 * */
		public function get dateStr():String
		{
			var dateFormatter:DateFormatter = new DateFormatter();
			dateFormatter.formatString = "DD-MM-YYYY";

			return dateFormatter.format(_date);
		}

		public function set dateStr(value:String):void
		{
			_date = DateFormatter.parseDateString(value);
		}

		public function PersonVo(name:String = null, surname:String = null,
					date:Date = null, email:String = null, isSpecial:Boolean = false)
		{
			this.name = name;
			this.surname = surname;
			this._date = date;
			this.email = email;
			this.isSpecial = isSpecial;
		}
}

Następnie należy utworzyć DataGrid-a wraz z kolumnami wyświetlającymi informację, którymi jesteśmy zainteresowani oraz itemrenderer w którym obsłużone zostanie pogrubienie poszczególnych wierszy. Kod mojego przykładowego datagrid-a zawiera kolumnę „Imię i nazwisko” łączącą dwie właściwości z klasy PersonVo za pomocą funkcji anonimowej, email oraz sformatowaną datę urodzenia.

<?xml version="1.0" encoding="utf-8"?>
<mx:DataGrid xmlns:fx="http://ns.adobe.com/mxml/2009"
				 xmlns:s="library://ns.adobe.com/flex/spark"
				 xmlns:mx="library://ns.adobe.com/flex/mx"
				 itemRenderer="{new ClassFactory(DGItemRenderer)}">
	<mx:columns>
		<!--This simple anonymous function in DataGridColumn concatenates two string: name and surname from value object-->
		<mx:DataGridColumn headerText="Imię i Nazwisko"
		labelFunction="{function(personVo:PersonVo,
									dgColumn:DataGridColumn):String
						{return personVo.name + ' ' + personVo.surname; }}"
						   width="200"/>
		<mx:DataGridColumn headerText="Email"
						   dataField="email"
						   width="250"/>
		<mx:DataGridColumn headerText="Data urodzenia"
						   dataField="dateStr"
						   width="120"/>
	</mx:columns>
	<fx:Script>
		<![CDATA[
			import dataGridItermRenderers.DGItemRenderer;
			import vo.PersonVo;
		]]>
	</fx:Script>
</mx:DataGrid>

Najważniejsza część całej zabawy-jest klasa itemrenderera zawierająca przesłoniętą właściwość „data”. W jej wnętrzu sprawdzany jest typ dostarczonej wartości ponieważ podczas tworzenia DataGrida przekazywane są do niej obiekty poszczególnych kolumn, którymi nie byłem zainteresowany. W dalszej kolejności właściwość wykonuję się przy wypełnieniu każdej komórki poszczególnych kolumn co jak widać daję możliwość sterowania wyglądem ich zawartości poprzez ręczne ustawienie styli.

public class DGItemRenderer extends DataGridItemRenderer
{
		public function DGItemRenderer()
		{
			super();
		}

		/**
		 * Property is execute on each column in each row
		 *
		 * @param PersonVo|Object value Object using in item renderer
		 */
		override public function set data(value:Object):void
		{
			if (value is PersonVo && value)
			{
				//In this property i check if PersonVo is special
				//When isSpecial == true i bold cell in this column
				var boldValue:String = (PersonVo)(value).isSpecial == true ? "bold" : "normal";
				setStyle("fontWeight", boldValue);
			}
			super.data = value;
		}
}

Wynikiem tych zabiegów są oczekiwane pogrubione zawartości wierszy w DataGridzie. link

Porównywanie dat w ActionScript

Implementując pewną funkcjonalność stanąłem przed prostym problemem porównania dwóch dat. W ActionScript do tego celu służy klasa ObjectUtil,
a w niej metoda compareDate.

public static function dateCompare(a:Date, b:Date):int
{
				if (a == null && b == null)
					return 0;

				if (a == null)
					return 1;

				if (b == null)
					return -1;

				var na:Number = a.getTime();
				var nb:Number = b.getTime();

				if (na < nb)
					return -1;

				if (na > nb)
					return 1;

				return 0;
}

Jam można zauwazyć wewnątrz metody daty porównywane są na podstawie zwróconej ilości milisekund od roku 1970 metodą getTime. Potrzebowałem jednak czegoś bardziej specyficznego, mianowicie chciałem porównać daty pomijając przy tym czas (godziny, minuty itd.). Utworzyłem w tym celu nową metodę w której trzeci parametr określa taką możliwość.

public function compareDate(a:Date, b:Date, compareWithTime:Boolean=false):int
{
				var aa:Date;
				var bb:Date;

				if (a == null && b == null)
				{
						return 0;
				}

				if (a == null)
				{
						return 1;
				}

				if (b == null)
				{
						return -1;
				}

				if (compareWithTime)
				{
						return ObjectUtil.dateCompare(a, b);
				}
				else
				{
						//porownanie bez czasu
						//tworze nowe obiekty dat wylaczajac przy tym czas
						aa = new Date(a.fullYear, a.getMonth(), a.getDate());
						bb = new Date(b.fullYear, b.getMonth(), b.getDate());

						return ObjectUtil.dateCompare(aa, bb);
				}
}

Przeprowadziłem test dla dwóch dat i otrzymałem następujące wyniki:

var dOne:Date = new Date(2010, 2, 20, 12, 12, 12);
var dTwo:Date = new Date(2010, 2, 20, 12, 11, 12);

var resultOne:int = compareDate(dOne, dTwo, false);
var resultTwo:int = compareDate(dOne, dTwo, true);

//resultOne = 0; dOne = dTwo
//resultTwo = 1; dOne > dTwo