Archive for February, 2010

Grey Line

The first two weeks of March I’ll be vacationing in training rooms – teaching and learning Flex.  I used the word vacationing because I love this part of my work the most.

March 1-2: Advanced Master Class on Flex in Brussels, Belgium. This public 2-day training becomes more and more popular. During the last 8 months we’ve taught this class in New York, Boston, Toronto, London, and Moscow. To the best of my knowledge, no one else offers such an advanced curriculum as public training.  On the night of March 2 I’ll be co-speaking at the Belgium Flex Users Group.

Here’s something you may not know. Viktor Yanukovich, the newly elected President of the Ukraine will visit Belgium on March 1. The real reason is not to  meet political leaders of Belgium and European Union, but to attend our class to become more flexible and invite Farata Systems to teach the same class in Ukraine in June of 2010. We’ll definitely consider this.

March 7-10: On arrival from Belgium, I’ll just have time to laundry my Farata t-shirts and have a couple of dinners with my family, and then board the next flight to San Jose, CA. Yep, it’s time for 360Flex conference, which as of today is my favorite Flex gathering. This is a No BS event. For independent developers by independent developers. 40 sessions, 2 panels, 4 Sunday Hands-On sessions. Networking. Beer. Good energy.  Solid technical content.I even recorded a 40-sec video to share with you my excitement!

I’ll deliver an interesting and useful for enterprise Flex developers talk titled “Boring presentation on Flex libraries and modules”.  The rest of the time I’ll spend in the meeting rooms listening to what other developers are up to.

In the evening, I’ll be glad to join you for a Johny Walker. Be there. Join several hundreds of Flex developers who are in the know!

Yours truly
Yakov Fain

Comments (1)

 

Grey Line

Coming across several blogs that hint that custom Spark skins should not be created via OOP inheritance I felt challenged. After all, Spark skins are using inheritance already, so why should I be stripped of something I am so used to? In this post , I will create a PictureButton component that injects image into the Spark Button:

<PictureButton label="My PictureButton"  pictureGroupName="statistic" skinClass="PictureButtonSkin" />

This is how the button looks PictureButtonand you can run view-source enabled demo application if you, like me, do not have patience for the wordy blogs. PictureButton component features pictureGroupName property, which represents the common “group name” of three images for “up”, “over” and “down” states. Mangled with the “_up”, “_over” and “_down” suffixes it should yield the real image URLs (e.g. assets/statistic_up.png, assets/statistic_down/png etc.) :

Clearly, creating public class PictureButton extends spark.skins.sparkButton is a must, if anything – just to carry arround the pictureGroupName. Then we have three choices for the PictureButtonSkin:
a) create a brand new skin (this had been done before)
b) massage the clone of spark.skins.spark.ButtonSkin
c) build a descendant (in OOP terms) of the Spark skin.

This post is focusing on the last two approaches, particularly highlighting the OOP way.

Making Picture Button Skin By Cloning And Massaging Spark Button Skin

Massaging the original skin we redirect the HostComponent to point to our forthcoming PictureButton. Then, in the last, 8-th layer of original skin, we wrap the labelDisplay skin part with the VGroup and inject a bitmapImage. The “up”, “over” and “down” variants of the bitmapImage.source are bound to the bitmaps carried within the “hostComponent” :

<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" 
             xmlns:fb="http://ns.adobe.com/flashbuilder/2009" minWidth="21" minHeight="21" alpha.disabled="0.5"
	 >
	<fx:Metadata>
		<![CDATA[
		[HostComponent("PictureButton")]
		]]>
       <!-- The rest of the original spark.skins.spark.ButtonSkin - till layer 8  is omitted for brevity -->
	.  .  .  .  .
	<!-- layer 8: text -->
	<!-- @copy spark.components.supportClasses.ButtonBase#labelDisplay -->
	<!-- WE COMMENTED OUT ORIGINAL labelDisplay
         <s:Label id="labelDisplay"
			 textAlign="center" verticalAlign="middle"
			 maxDisplayedLines="1"
			 horizontalCenter="0" verticalCenter="1"
			 left="10" right="10" top="2" bottom="2">
	</s:Label> 
         -->
	<s:VGroup  horizontalAlign="center"  verticalAlign="middle" left="3" right="3"
			   paddingTop="3" paddingBottom="3" 
			   paddingLeft="3" paddingRight="3" 
				   >			
		<s:BitmapImage  id="bitmapImage" 
			  source.up="{hostComponent.bitmapImageUp}"
			  source.over="{hostComponent.bitmapImageOver}"
			  source.down="{hostComponent.bitmapImageDown}"
		/>
 
		<s:Label id="labelDisplay" left="10" right="10" top="2" bottom="2"
			  textAlign="center" verticalAlign="middle" 
			  maxDisplayedLines="1" 
		/>
	</s:VGroup>		
</s:SparkSkin>

Now, let’s supplement this skin with the matching PictureButton code.


PictureButton Component
To make the very PictureButton we extend the Spark button and instantly create three SWFLoaders to facilitate load of three images:

public class PictureButton extends Button	{
      private var loaderUp:SWFLoader;
      private var loaderDown:SWFLoader;
      private var loaderOver:SWFLoader;
 
       public function PictureButton()
	{
	        super();
		loaderUp = new SWFLoader;
		loaderDown = new SWFLoader();
		loaderOver = new SWFLoader();
		loaderUp.addEventListener(Event.COMPLETE, onLoadComplete);
		loaderDown.addEventListener(Event.COMPLETE, onLoadComplete);
		loaderOver.addEventListener(Event.COMPLETE, onLoadComplete);
	}
...

Changing of the pictureGroupName will result in loading of the pictures, according to the setter below:

		private var _pictureGroupName:String;
 
		[Bindable]
		public function set pictureGroupName (value:String):void {
			if (_pictureGroupName !== value) {
				_pictureGroupName = value;
				loadPictures();
			}
		}
 
		public function get pictureGroupName():String {
			return _pictureGroupName;
		}
 
		private function loadPictures():void {
			if (loaderUp) loaderUp.load("assets/" + pictureGroupName + ".png")
			if (loaderDown) loaderDown.load("assets/" + pictureGroupName + "_down.png");
			if (loaderOver) loaderOver.load("assets/" + pictureGroupName + "_over.png");
		}

Finally, as images get loaded we populate corresponding bindable bitmapImage properties that our custom skin so much depends on. Note, that being public these properties can be set from outside; you can avoid using pictureGroupName whatsoever and, in particular, you may load all your images from module classes or CSS styles etc.

		[Bindable] public var bitmapImageUp:Object;
		[Bindable] public var bitmapImageDown:Object;
		[Bindable] public var bitmapImageOver:Object;
 
		private function onLoadComplete(event:Event):void {
			if (event.target == loaderUp) {
				bitmapImageUp = event.target.content;
			} else if (event.target == loaderDown) {
				bitmapImageDown = event.target.content;
			} else if (event.target == loaderOver) {
				bitmapImageOver = event.target.content;
			}
		}
 
}

OK. Both skin and control are done, but there is an afterthought: WHAT IF THE BASE SKIN CHANGES?
Obviously, we will be out of sync. Hence the need for OOP approach.

Making PictureButtonSkin WITH INHERITANCE


All we did in the cloned Skin was replacing original labelDisplay with the VGroup containing both labelDisplay AND bitmapImage.
We did it with the declarative MXML, however the same can be done in ActionScript:

package
{
        import ...
	public class PictureButtonSkin extends ButtonSkin      {
 
                protected override function childrenCreated():void {
		       super.removeElement(labelDisplay);
		       addElement(createVGroup());
	               super.childrenCreated();
	        }
	        [Bindable]
		public var bitmapImage : spark.primitives.BitmapImage;
 
		private function createVGroup():VGroup
		{
			var temp : spark.components.VGroup = new spark.components.VGroup();
			temp.horizontalAlign = "center";
			temp.verticalAlign = "middle";
			temp.left = 3;
			temp.right = 3;
			temp.paddingTop = 3;
			temp.paddingBottom = 3;
			temp.paddingLeft = 3;
			temp.paddingRight = 3;
			temp.mxmlContent = [createBitmapImage(), createLabel()];
			return temp;
		}
 
		private function createBitmapImage() : spark.primitives.BitmapImage
		{
			bitmapImage = new BitmapImage();
			return bitmapImage;
		}
 
		private function createLabel() : spark.components.Label
		{
			labelDisplay  = new Label();
			labelDisplay.left = 10;
			labelDisplay.right = 10;
			labelDisplay.top = 2;
			labelDisplay.bottom = 2;
			labelDisplay.maxDisplayedLines = 1;
			labelDisplay.setStyle("textAlign", "center");
			labelDisplay.setStyle("verticalAlign", "middle");
			labelDisplay.id = "labelDisplay";
			return labelDisplay;
		}
}

Now, notice that we did not populate the source of the bitmapImage. The reason is that by diving into ActionScript we lost “luxury” of state-related code generation for “source.up”, “source.over” and “source.down”. To complement this we have to listen to “stateChanging” event and adjust the source ourselves:

 	public class PictureButtonSkin extends ButtonSkin
	{
		public function PictureButtonSkin(){
			super();
			addEventListener(StateChangeEvent.CURRENT_STATE_CHANGING, onStateChanging);
		}
                .  .  .  .
		private function onStateChanging(event:StateChangeEvent):void {
			switch (event.newState){
				case "down":
					bitmapImage.source = hostComponent["bitmapImageDown"];
					break;
				case "over":
					bitmapImage.source = hostComponent["bitmapImageOver"];
					break;
				case "default":
					bitmapImage.source = hostComponent["bitmapImageUp"];
			}
		}
}

And that is pretty much it.


Conclusions

Programmatic extension of skins is not that hard. As a developer, I prefer the OOP way to copy/paste.
I am well aware that it breaks the Designer/Developer separation of concerns as it is currently (I stress, currently) envisioned by Adobe.
I am well aware that using non-vector graphics in the first place, instead of Rect and Line primitives, has it’s own drawbacks.
Yet, being realistic, most often than not I will not have a designer to draw for me.
Neither do I get paid for drawing: I am a coder.

Why I like OOP? Less code.

Demo Application
SourceView

I’d like to use this opportunity and invite Flex developers living in Europe to attend our Advanced Flex Master Class in Brussels, Belgium on March 1 and 2, 2010.

Victor Rasputnis

Comments (2)