Main

Creating a Custom Component and Skins in Flex 4

The basic idea behind the Spark component model in Flex 4 is to entirely decouple the behavior of a component from its visual representation. You code the behavior of a component in one class and its visual representation in interchangeable MXML skin classes. This new architecture leads to lighter weight and easier to customize components.

As a simple example, I created a Spark version of the PhotoInput component I built for some of my Flex 3 samples (Salesbuilder and inSync). The PhotoInput component allows the user to take pictures using the webcam.

Here is the PhotoInput component with a minimalistic skin. Click the Start Camera button to start your webcam, and the Take Picture button to take a picture.

<components:PhotoInput skinClass="skins.PhotoInputSkin" width="200"/>

… and here is the same PhotoInput component with a different skin. Click the small webcam icon in the upper left of the component to start the webcam.

<components:PhotoInput skinClass="skins.PhotoInputSkin2" width="200"/>

As you can see in the main application class, this is the same PhotoInput component used with two different skins: PhotoInputSkin and PhotoInputSkin2.

PhotoInputSkin.mxml is defined as follows:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:s="library://ns.adobe.com/flex/spark"
		xmlns:mx="library://ns.adobe.com/flex/mx">

	<!-- host component -->
	<fx:Metadata>
		[HostComponent("components.PhotoInput")]
	</fx:Metadata>

	<s:states>
		<s:State name="capturing"/>
		<s:State name="normal"/>
	</s:states>

	<s:layout>
		<s:VerticalLayout/>
	</s:layout>

	<mx:VideoDisplay id="videoDisplay" width="{hostComponent.width}" height="{hostComponent.width}"/>

	<s:BitmapImage id="image" width="{hostComponent.width}" height="{hostComponent.width}"/>

	<s:Button id="startCameraButton" label="Start Camera" width="100%" includeIn="normal"/>
	<s:Button id="stopCameraButton" label="Stop Camera" width="100%" includeIn="capturing"/>
	<s:Button id="takePictureButton" label="Take Picture" width="100%" includeIn="capturing"/>

</s:Skin>

… and PhotoInputSkin2.mxml is defined as follows:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:s="library://ns.adobe.com/flex/spark"
		xmlns:mx="library://ns.adobe.com/flex/mx">

	<fx:Script>
		<![CDATA[

			private function takePicture():void
			{
				// Show the picture that was just taken for 2 seconds,
				// then show the webcam capture again
				image.visible = true;
				var myTimer:Timer = new Timer(2000, 1);
				myTimer.addEventListener(TimerEvent.TIMER, timerHandler);
				myTimer.start();
			}

			private function timerHandler(event:TimerEvent):void
			{
				if (currentState == "capturing") image.visible = false;
			}


		]]>
	</fx:Script>


	<fx:Declarations>
		<mx:SoundEffect id="soundEffect" source="@Embed(source='assets/camera_click.mp3')" useDuration="false" />
	</fx:Declarations>

	<s:states>
		<s:State name="capturing"/>
		<s:State name="normal"/>
	</s:states>


	<!-- host component -->
	<fx:Metadata>
		[HostComponent("components.PhotoInput")]
	</fx:Metadata>

	<s:layout>
		<s:BasicLayout/>
	</s:layout>

	<s:Rect radiusX="8" radiusY="8" top="0" bottom="0" left="0" right="0">
		<s:fill>
			<s:LinearGradient rotation="90">
				<s:GradientEntry color="0x606060" alpha="1" ratio="0" />
				<s:GradientEntry color="0x303030" alpha=".8" ratio="1" />
			</s:LinearGradient>
		</s:fill>
	</s:Rect>

	<s:Group  top="4" bottom="4" left="4" right="4">

		<s:layout>
			<s:BasicLayout/>
		</s:layout>

		<mx:VideoDisplay id="videoDisplay" width="{hostComponent.width - 8}" height="{hostComponent.width}" includeIn="capturing"/>

		<s:BitmapImage id="image" width="{hostComponent.width - 8}" height="{hostComponent.width}"
					   source="@Embed('assets/male_user_gray.png')" visible.capturing="false"/>

		<s:Button id="startCameraButton" skinClass="skins.StartWebCamButtonSkin" toolTip="Start Camera"/>
		<s:Button id="stopCameraButton" skinClass="skins.StopWebCamButtonSkin" includeIn="capturing" toolTip="Stop Camera"/>
		<s:Button id="takePictureButton" skinClass="skins.TakePictureButtonSkin" label="Take Picture" width="100%" bottom="0" includeIn="capturing"
				  toolTip="Take Picture"
				  mouseDownEffect="{soundEffect}"
				  click="takePicture()"/>


	</s:Group>

</s:Skin>

The PhotoInput class provides the basic behavior of the component (start and stop the camera, take the picture, etc.) and is defined as follows:

package components
{
	import flash.display.BitmapData;
	import flash.events.MouseEvent;
	import flash.media.Camera;

	import mx.controls.VideoDisplay;
	import mx.graphics.ImageSnapshot;

	import spark.components.Button;
	import spark.components.supportClasses.SkinnableComponent;
	import spark.primitives.BitmapImage;

	[SkinState("capturing")]
	public class PhotoInput extends SkinnableComponent
	{
		private var camera:Camera;

		private var isCameraOn:Boolean = false;

		[SkinPart(required="true")]
		public var videoDisplay:VideoDisplay;

		[SkinPart(required="true")]
		public var image:BitmapImage;

		[SkinPart(required="false")]
		public var takePictureButton:Button;

		[SkinPart(required="false")]
		public var startCameraButton:Button;

		[SkinPart(required="false")]
		public var stopCameraButton:Button;

		public function PhotoInput()
		{
			super();
		}

		override protected function partAdded(partName:String, instance:Object):void
		{
			super.partAdded(partName, instance);

			if (instance == startCameraButton)
			{
				startCameraButton.addEventListener(MouseEvent.CLICK, startCameraButton_click);
			}
			if (instance == stopCameraButton)
			{
				stopCameraButton.addEventListener(MouseEvent.CLICK, stopCameraButton_click);
			}
			if (instance == takePictureButton)
			{
				takePictureButton.addEventListener(MouseEvent.CLICK, takePictureButton_click);
			}
		}

		override protected function partRemoved(partName:String, instance:Object):void
		{
			super.partRemoved(partName, instance);

			if (instance == startCameraButton)
			{
				startCameraButton.removeEventListener(MouseEvent.CLICK, startCameraButton_click);
			}
			if (instance == stopCameraButton)
			{
				stopCameraButton.removeEventListener(MouseEvent.CLICK, stopCameraButton_click);
			}
			if (instance == takePictureButton)
			{
				takePictureButton.removeEventListener(MouseEvent.CLICK, takePictureButton_click);
			}
		}

		override protected function getCurrentSkinState():String
		{
			if (isCameraOn)
			{
				return "capturing";
			}

			return "normal";
		}

		private function startCamera():void
		{
			isCameraOn = true;
			invalidateSkinState();
			callLater(attachCamera);
		}

		private function attachCamera():void
		{
			camera = Camera.getCamera();
			videoDisplay.attachCamera(camera);
		}

		private function stopCamera():void
		{
			isCameraOn = false;
			invalidateSkinState();
			videoDisplay.attachCamera(null);
		}

		private function takePicture():void
		{
			var bd:BitmapData = ImageSnapshot.captureBitmapData(videoDisplay);
			image.source = bd;
		}

		private function startCameraButton_click(event:MouseEvent):void
		{
			startCamera();
		}

		private function stopCameraButton_click(event:MouseEvent):void
		{
			stopCamera();
		}

		private function takePictureButton_click(event:MouseEvent):void
		{
			takePicture();
		}

	}
}

Source Code

You can download the source code of the application here.

More Information

To learn more about skins, make sure you read Ryan Frishberg’s great article on DevNet.

Share this Article:

23 Responses to Creating a Custom Component and Skins in Flex 4

  1. michaelB January 23, 2010 at 10:33 pm #

    nice work…thanks for the post.

  2. Yucheng March 28, 2010 at 8:34 am #

    Before I read this, I feel so confused about the skin. Now I think I’ve known some about the mystery of flex skin.

  3. MikeLac August 15, 2010 at 6:46 pm #

    Learned some valuable info on creating skins in flex 4. Your post is very valuable! Thanks so much! :-)

  4. Cyril Karpenko August 19, 2010 at 5:10 am #

    Thank you for this article! It’s was very helpful to me.

  5. Alex August 20, 2010 at 9:46 am #

    Cool post dude!

    I’m still trying to do the same, but I don’t know why is not working properly for me..
    GOOOOD!! hehehhe

  6. Agustin Lopez August 22, 2010 at 12:28 pm #

    Great guide Christophe, normally people are code in Flex 4 as they did in Flex 3 and the purpose of the component model in Flex 4 is entirely different and I believe MUCH better.

    Thanks for this guide, it will help a few understand how Flex 4 is supposed to be used.

  7. Bob October 13, 2010 at 11:35 am #

    Great post. I have been looking for something like this for the last couple days.

  8. victoria October 18, 2010 at 4:31 pm #

    This was really helpful. I tried to switch over the mx:VideoDisplay to use Spark VideoDisplay, but there doesn’t appear to be a spark equivalent of attachCamera. Do you know what I should use instead?

  9. Arthur Clifford October 29, 2010 at 11:16 pm #

    I don’t know if there’s any reason not to do this, but you can avoid having to do your AddPart and RemovePart overrides by setting the click handlers for your buttons in the mxml to use hostComponent.FunctionName()

    so:


    could become

  10. Arthur Clifford October 29, 2010 at 11:19 pm #

    Sorry, my examples got cut, basically just add click=”hostComponent.startCamera()” to whatever button in any skin you want to perform the start camera option. If you do that and you aren’t referring tot he start camera button in any other functionality you don’t have to declare it a skin part or include it in the add parts override.

  11. Gil December 22, 2010 at 8:07 am #

    Wow, it sure surprised me! Nice post- I’ll use it.

  12. rama January 31, 2011 at 3:02 pm #

    Thank you for this article…It’s was very helpful to me.

  13. Cam Nils February 8, 2011 at 7:27 am #

    Great guide Christophe. I am familiar with Flex 3 and am trying out your example to see if I can get some of my components working properly with Flex 4 the same way. Great example, thanks again!

  14. Daniel March 2, 2011 at 3:27 am #

    Hi, nice post, thank you…i wrote something similiar last week (but in spanish) and also a example of creating a skin with Adobe Catalyst. Here at the company i work for are thinking of moving from Flex3 to Flex4. Its a huge project so its going to take a while probably, but i think it will pay…

  15. anon March 2, 2011 at 11:55 am #

    thx

  16. YASIN April 26, 2011 at 11:26 pm #

    im from iran
    i wanted how can make a skin in visuall basic
    sorry if u know send to this email plz
    if know sombody call to this email
    thanks for ur website
    bye

  17. YASIN April 26, 2011 at 11:29 pm #

    tatotato504@yahooo.com

  18. rizwan June 14, 2011 at 4:10 am #

    good example.

  19. fdsf April 18, 2012 at 11:54 am #

    when change the datagrid column

  20. jalaludhin April 23, 2013 at 5:24 am #

    Thanks

Trackbacks/Pingbacks

  1. Spark Component Model 101 Links - One Hungry Mind - January 23, 2010

    [...] Christophe Coenraets also written a super article Creating a Custom Component and Skins in Flex 4 [...]

  2. Cool ItemRenderers Made Easy in Flex 4 - January 28, 2010

    [...] Christophe Coenraets Rich Internet Applications, Flex, AIR, Java Skip to content BioUsing Flex with Spring « Creating a Custom Component and Skins in Flex 4 [...]

  3. Good example for learning Flex 4 Skining - January 28, 2010

    [...] The Flex 4 learning curve will be from Spark and its new skin programming model. Here is another source to learn a few things from, Christonphoe Coenraets’s post Creating a Custom Component and Skins in Flex 4. [...]

Leave a Reply


8 + = 14

Powered by WordPress. Designed by Woo Themes