How to Skin QNX UI Components (Basic)

by Retired ‎08-25-2011 04:12 PM - edited ‎06-01-2012 10:07 AM (10,580 Views)


The look and feel of the UI components used in your application can be easily changed by skinning. This guide will walk you though creating your own skins with a variety of techniques using a collection of code samples and helpful screen shots. We will create our own custom component and skin it, but these same techniques can be used on the existing QNX components. To see the skins in action, grab the full sample application attached to this article. It is a simple memory game you can play.


Skinning Basics

Each QNX component can have several different states associated with it. Each of these states can have it's own visual look, represented by a Sprite. For example, if you have a button it might have a separate Sprite for the UP, DOWN, SELECTED, and DISABLED states (To see the possible states, take a look at the SkinStates class). A Skin object is will let you assign these sprites to each state, and manage the act of displaying the correct one for you. To accomplish this, extend UISkin and override the initializeStates method:


		override protected function initializeStates():void 
			var upSkin:Sprite = new BasicUpSkin();
			setSkinState(SkinStates.UP, upSkin);
			var downSkin:Sprite = new BasicDownSkin();
			setSkinState(SkinStates.DOWN, downSkin);
			var disabledSkin:Sprite = new BasicDisabledSkin();
			setSkinState(SkinStates.DISABLED, disabledSkin);


Once that is set up, this Skin can be assigned to any SkinnableComponent via the setSkin method, and then whenever the object's state is changed, the proper Sprite will be displayed. We'll now go through a few different ways of creating these Sprites for use on our custom SkinnableComponent, Target.


9-Slice Scaling

9-Slice scaling is the simplest and least powerful method of skinning. It lets you draw a small image to use as a skin, and then strech the middle parts of it so the corners remain static.You can see it in action in the BasicSkin mode of the sample game. It is accomplished by embeding a graphic object into the application (like a PNG) and dividing it into 9 distinct slices. This lets the UI framework know which parts of the skin can be streched, and which must be preserved and drawn as-is.


This is the actual skin used for the DISABLED state of the BasicSkin:


And here it is in the game, on the bottom left:



How did that happen? Let's take a closer look at the skin:



This is the skin magnified about 32x. The grid lines show the individual pixels, and the checkerboard pattern shows where it's trasnparent. This is how we import it into the game:


		[Embed(source="assets/basic disabled skin.png", scaleGridTop="9", scaleGridBottom="11", scaleGridLeft="9", scaleGridRight="11")]
		private var BasicDisabledSkin:Class;


Embed is a preprocessor directive that lets us take the the file specified by the source argument, and embed it directly into the SWF file as the class defined beneath it (in this case BasicDisabledSkin). When we create an instance of that class in our initializeStates method we'll get this image as a Sprite with those four scaleGrid properties defined and telling the UI framework where the corners are. For example, here is the same image with red lines drawn where each scaleGrid property is defined.



This gives us 9 distinct regions (or "slices"):



The four areas marked "corner" are drawn as-is every time. They don't get streched at all. The two red slices (each one pixel tall, and 9 pixels wide) are streched vertically if the object we are skinning is taller than the skin. The yellow area is streched horizontally if the object is long. The purple area in the middle is what gets streched to fill everything in the center.


Here we can see the Target skinned and drawn to fill size (the black is the background from the game, remember the skin has transparent edges to give rounded corners):



And now if we highlight those 9 slices like we did on the last picture of the skin:



You can see that the blue corners remained the same, but the red and yellow areas were streched to give a nice border, and the purple area was streched to fill out the middle.


Alpha Skins

9-Slice scaling is a nice way to get skins with things like rounded corners and solid borders, but if we want to use different colours, we would have to have a different PNG for each one. This can quickly add up to a lot of extra work. To get around it, we'll create a 9-slice skin, but draw it as an alpha mask rather than a solid colour.


Here are the three alpha skins: UP, DOWN, and DISABLED



Let's zoom in on the DISABLED skin again:



It's the same shape as the basic skin, but instead of having a solid white border with a solid grey center the border is transparent and the center is partially trasparent. The corners are solid black to match the background of the game. The UP state (default) has a solid black center, and the DOWN state (when the user presses the target) has a completely transparent center.


To take advantage of this, we will modify the initializeStates method to call our own drawBackground method on each sprite:

		override protected function initializeStates():void  
			var upSkin:Sprite = new AlphaUpSkin();
			setSkinState(SkinStates.UP, upSkin);
			var downSkin:Sprite = new AlphaDownSkin();
			setSkinState(SkinStates.DOWN, downSkin);
			var disabledSkin:Sprite = new AlphaDisabledSkin();
			setSkinState(SkinStates.DISABLED, disabledSkin);


The drawBackground method is a simple method that draws a solid, coloured rectangle on the sprite:

		private function drawBackground(sprite:Sprite):void {;,0,sprite.width,sprite.height);;


Our 9-slice image then gets overlayed on top. It only allows certain amounts of the background colour through. This is what the disabled state looks like in game:



As you can see, the targets now have coloured borders, and when DISABLED the center is a darker version of the target's colour. When UP it's black, and when DOWN it's the same colour as the border.


9-Slice scaling is simple to use, but fairly limited. Because of the streching, you won't be able to use it to create things like gradients or checkerboard patterns. For example, take a look at this skin:



Each box is a 10x10 slice, giving us a 30x30 skin. If we try and apply it to a component that is 500x250 we get this:



The letters in the corners look fine, but the one in the top middle is unreadable. The checkerboard on the right side is completely distorted, as is the nice circle we had on the left. Our X in the center doesn't look so hot blown up to this size either.


In the next article, we will take a look at how you can create skins with gradients and complicated shapes using the drawing API. Check out How to Skin QNX UI Components (Advanced), or see all the skins in action in the attached sample application, Sequence.