The Basics of Skinning and Themes

UPDATED 6/16/2011

  • All screen shots reference Flash Builder 4.5
  • All projects and examples are build with Flash Builder 4.5

This article is part two of a series and builds on exercises and concepts presented in part one. If you have not already read the first part in this series, set up the Acrobat X Portfolio SDK and successfully built the “MyFirstNavigator” project, please stop reading this article now and go to the Your First Navigator Project article.

Background:

Adobe Acrobat X PDF Portfolio Layouts (Navigators) can have a variety of “looks” applied to them called Visual Themes in the Acrobat User Interface or “Skins” in Flash Builder 4.5 termionology. Because the Visual Themes in Acrobat leverage the Spark Skinning Model, for the purposes of this document, Skins and Visual Themes will be use inter-changeably.

Skins can be of two types; “flex" and “flash”. Skins of type “flex” (FlexSkins) are more flexible (no pun intended) because they can have transitions and effects and the position of elements on the card can be changed. However, they must be authored in Flash Builder and must be compiled with your Navigator project. Skins of type “flash” (FlashSkins) are easy to author in Flash Professional, but all objects in the Navigator layout have a fixed size and position. Both skin types are published as .SWF files.

Note: This page focuses on creating custom FlexSkins. Read “Guide to Creating Custom PDF Portfolio Themes for Adobe Acrobat X” for information on how to create FlashSkins.

The screen shot below shows an example of a PDF Portfolio with two different visual themes. The first is the default Clean skin and the second is a custom skin created for the Meridien Conference. The content is the same but you can see how a custom theme can make the your PDF Portfolio highly branded and much more visually appealing.

DefaultLinear.png
“Clean” Visual Theme

MeridienLinear.png
Custom Visual Theme

Getting Started with Themes

Ideally, you are already familiar with the concepts around skinning in Flash Builder 4. If you are not, the content below may less meaningful for you because it only covers the Portfolio SDK related aspects of skinning. If you are not familiar with skinning, you may want to learn or brush up using the Skinning Spark Components section of Flex in a Week video training.

The items that make up a Portfolio are accessed using the interface defined in the class IAttachment. "Cards" are skinnable renderers for the IAttachment class. Every attachment in a Portfolio is represented by a card that shows, where possible, the user an image of the attachment, as well as metadata information about the attachment. Cards provide a preview of the data for PDF documents, images, Office documents, video and audio attachments.

There are a variety of cards for basic file attachments, folders, previews, and so on. This article discusses how to create a custom skin for a card in a PDF Portfolio.

The Navigator Project Wizard will automatically create the basics of a custom theme by automatically adding the CustomTheme.mxml, CustomCardSkin.mxml files to your project and add the appropriate xml snippets to the properties.xml file in your project’s navfiles/navigator folder. I’ll discuss the properties.xml file later in this article and how to skin more than just the card in the next article. We’ll begin by picking up where we left off in the previous article but you can’t just duplicate the “MyFirstNavigator” project, rename it and expect everything to work. The Navigator Project Wizard automatically generates code in the navfiles folder based on the project name you enter. It’s good practice, though not required, to have the project name, the resulting .nav file and the name that appears in the Acrobat X UI be the same.

  1. Create a new Navigator Project called “MyFirstTheme” – remember to set the Flex SDK version to 4.1 and the Run/Debug Settings as described in part one of this series.
  2. Copy the entire Src folder from the “MyFirstNavigator” project and over-write the one that was automatically created in the new project so that you can continue with the work you did in part one.
  3. Press Run to run the project in the Preview application so that you can get a reminder of what the default skin looks like.
  4. Open the Properties panel in the Preview application. The Visual Themes tab should show that Clean is the selected Theme.
  5. Use the Theme drop-down to select the My Theme Theme. You’ll see that the card doesn’t change too much but the background is now a dark blue rathe than grey. This is the theme we’ll work on in the rest of this article.
  6. You can close the Preview application now.


“My Theme” Visual Theme

In this exercise I want to modify the card to better match the branding I want by:

  • removing the rounded corners so that I have nice, sharp, corners on my card
  • remove the border
  • make the header, the blue gradient at the top, a flat color
  • move the card buttons from inside the card to the outside

The final step is to change the name of the theme to Meridien Corporate rather than My Theme

Modifying the Card Skin

One issue with the Acrobat X Portfolio SDK is that the Design Mode in Flash Builder is not terribly useful for skinning Components in the Portfolio Framework. Most of the objects in the framework rely on Acrobat to supply bitmaps for the visual elements and the Flash Builder authoring environment doesn’t have the hooks to do that. You’ll be spending much of your time just looking at code while skinning your components.

  1. Open CustomCardSkin.mxml file.
  2. Locate the object with the id contentGroup (line 218). This is the card itself. All visual elements for the card are in this required group.You can either follow the steps below or replace the entire contentGroup with the code after step 12.
  3. Locate the object with the id borderShape and set the radiusX and radiusX properties to 0. This will remove the rounded corners of the borderShape. Since we don’t actually need the borderShape, you could simply remove or comment it out but there are references to that object elsewhere in the code that will cause errors when compiling. Once you are more familiar with the Portfolio SDK, you may want to go back and remove this code.
  4. Locate the object with the id foregroundShape and set all of the properties to 0 except alpha.
  5. Locate the object with the id titleBar. This is the blue gradient bar at the top of the card. Set it’s left, right, and top properties to 0
  6. Locate the FlashContainer object within the titleBar object with a swfclass of CardTitleBar. In the first rect object, remove the properties topLeftRadiusX, topLeftRadiusY, topRightRadiusX, and topRightRadiusY.
  7. Copy the color property from that rect object’s top gradientFill to get it onto the clipboard for step 9.
  8. Delete the LinearGradient object entirely.
  9. Add a SolidColor tag and paste the color property that you just copied. This will bind the color of the header to the Portfolio color palette.
  10. Delete the second rect in that group. It should look like the code below.
  11. <s: Recttop="0" left="0" right="0" bottom="7" bottom.smallStates="5">
    	<s:fill>
    		<s:SolidColor color="{hostComponent.colorPalette.primaryColor}"/>
    	</s:fill>
    </s:Rect>
  12. Locate the object with the id preview and set the right property to 15 to match the left and bottom border. Remember, we’re going to move the buttons to outside the card so we don’t need that large a marging on the right.
  13. Locate the object with the id controlButtons and set the right property to -40 to move the group outside the boundary of the card.
	<s:Group id="contentGroup" top="0" bottom="0" left="0" right="0">
<supportClasses:FlashContainer swfClass="NavCard"
swfClass.selectedStates="NavCardSelected"
swfClass.overStates="NavCardOver"
width="100%" height="100%">
<!-- Dropshadow below the card -->
<s:Ellipse id="bottomShadow" left="-15" right="-15" bottom="-5" height="9"
alpha=".8" visible="false" includeInLayout="false">
<s:fill>
<s:RadialGradient>
<s:GradientEntry color="0x000000" ratio="0" />
<s:GradientEntry color="0x000000" ratio="1" alpha=".5" />
</s:RadialGradient>
</s:fill>
</s:Ellipse>

<s:Rect id="backgroundShadow" left="0" right="0" top="0" bottom="0"
radiusX="12" radiusY="12"
visible="false">
<s:fill>
<s:SolidColor />
</s:fill>
</s:Rect>

<!-- Background Shape -->
<s:Group id="backgroundGroup" left="0" right="0" top="0" bottom="0">

<s:Rect id="borderShape" left="0" right="0" top="0" bottom="0" alpha=".8"
radiusX="0" radiusY="0">
<s:fill>
<s:SolidColor id="borderColor"
color="{hostComponent.colorPalette.cardBorder}"
color.overStates="{hoverColor}"
color.selectedStates="{selectedColor}"
color.infoStates="0xCCCCCC"/>
</s:fill>
</s:Rect>

<s:Rect id="foregroundShape"
left="0" right="0" top="0" bottom="0"
left.smallStates="0" right.smallStates="0" top.smallStates="0" bottom.smallStates="0"
radiusX="0" radiusY="0"
alpha=".4">
<s:fill>
<s:SolidColor color="0xFFFFFF"/>
</s:fill>
</s:Rect>

</s:Group>
</supportClasses:FlashContainer>


<!-- Title Bar -->
<s:Group id="titleBar" excludeFrom="infoStates"
left="0" right="0" top="0" height="54"
height.mediumStates="50"
left.smallStates="2" right.smallStates="2" top.smallStates="2" height.smallStates="35">

<supportClasses:FlashContainer width="100%" height="100%" swfClass="CardTitleBar">
<s:Rect top="0" left="0" right="0" bottom="7" bottom.smallStates="5">
<s:fill>
<s:SolidColor color="{hostComponent.colorPalette.primaryColor}" />
</s:fill>
</s:Rect>

<s:VGroup left="0" right="0" bottom="0" gap="0">

<s:Line width="100%">
<s:stroke>
<s:SolidColorStroke color="0x666666" caps="square" />
</s:stroke>
</s:Line>

<s:Rect height="5" height.smallStates="3" top="1" bottom="1" width="100%">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry id="colorOne" color="{hostComponent.colorPalette.accentColor}" ratio=".35" />
<s:GradientEntry color="{GradientUtil.secondaryColor(colorOne.color, -15)}" ratio="1" />
</s:LinearGradient>
</s:fill>
</s:Rect>

<s:Line width="100%">
<s:stroke>
<s:SolidColorStroke color="0x999999" caps="square" />
</s:stroke>
</s:Line>

</s:VGroup>
</supportClasses:FlashContainer>

<supportClasses:FieldTextInput id="displayText" data="{hostComponent.data}" fieldName="{FieldUtils.DISPLAY_NAME_FIELD_NAME}" fontWeight="bold" color="{hostComponent.colorPalette.primaryText}"
filters="{displayTextFilters}"
verticalCenter="-2" left="15" right="45"
fontSize="14" fontSize.mediumStates="13" fontSize.smallStates="11"
left.mediumStates="14" right.mediumStates="40"
left.smallStates="10" right.smallStates="10"/>

</s:Group>


<!-- Preview Component -->
<previews:Preview id="preview"
toolTip="{hostComponent.fileName}"
visible="true" visible.infoStates="false"
left="15" right="15" top="70" bottom="15"
left.mediumStates="24" right.mediumStates="24" top.mediumStates="65" bottom.mediumStates="14"
left.smallStates="12" right.smallStates="12" top.smallStates="40" bottom.smallStates="10"/>

<s:BitmapImage id="icon" smooth="false" excludeFrom="infoStates"
filters="{iconFilters}"
width="32" height="32"
width.smallStates="20" height.smallStates="20"
right="12" top="14"
right.mediumStates="12" top.mediumStates="11"
right.smallStates="8" top.smallStates="42"/>

<!-- Ribbon Group -->
<s:Group left="6" left.smallStates="3"
top="58" top.mediumStates="54" top.smallStates="36"
id="ribbon"
excludeFrom="infoStates"
rotation="-45"
filters="{shapeDS}">

<!-- Left triangle -->
<s:Path data="{'M 0 0 V' + (label.height + (label.height*.5)) + 'H ' + -(label.height + (label.height*.5)) + 'Z'}">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#F9E09C" />
<s:GradientEntry color="#F9C063" />
</s:LinearGradient>
</s:fill>
</s:Path>

<!-- Middle background -->
<s:Rect height="{label.height + (label.height*.5)}" width="100%">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#F9E09C" />
<s:GradientEntry color="#F9C063" />
</s:LinearGradient>
</s:fill>
</s:Rect>

<!-- Right triangle -->
<s:Path right="{-(label.height + (label.height*.5))}" data="{'M 0 0 V ' + (label.height + (label.height*.5)) + ' H ' + (label.height + (label.height*.5)) + 'Z'}">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#F9E09C" />
<s:GradientEntry color="#F9C063" />
</s:LinearGradient>
</s:fill>
</s:Path>
<s:RichText id="label"
text="{StringManager.instance.ribbonOpen}"
fontSize="18" fontSize.mediumStates="13" fontSize.smallStates="10"
fontWeight="bold" color="#333333"
verticalCenter="1.5" horizontalCenter="0"
filters="{whiteTxtDropShadow}"/>

</s:Group>

<!-- Righthand button bar -->
<s:VGroup id="controlButtons" right="-40" bottom="15" gap="12" includeIn="largeStates" >
<supportClasses:IconButton id="infoButton" defaultIcon="{InfoIcon}" baseColor="{hostComponent.colorPalette.primaryColor}" skinClass="com.adobe.portfolio.skins.CardButtonSkin" />
<supportClasses:IconButton id="extractOrDeleteButton" defaultIcon="{ExtractIcon}" editIcon="{DeleteIcon}" baseColor="{hostComponent.colorPalette.primaryColor}" skinClass="com.adobe.portfolio.skins.CardButtonSkin" />
</s:VGroup>


<!-- CARD BACK DISPLAYING - This is present in both large and info states to ensure a smooth transition -->
<s:Group id="metaLabels" left="0" right="0" top="0" bottom="0"
visible.largeStates="false" visible.infoStates="true" includeIn="largeStates, infoStates">

<s:Button id="infoCloseButton" top="10" right="15" skinClass="com.adobe.portfolio.skins.infoCloseBtnSkin" />

<s:VGroup top="32" bottom="30" left="15" right="15">

<s:HGroup width="100%">
<s:Label text="{FieldUtils.getDisplayNameByFieldName(collectionManager.fields, FieldType.FILE_NAME, true)}"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"
paddingTop="5"/>
<supportClasses:FieldTextInput id="fileName" data="{hostComponent.data}" fieldName="{FieldType.FILE_NAME}" width="100%" fontWeight="bold"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"/>
</s:HGroup>

<s:HGroup width="100%"
visible="{FieldUtils.getFieldByFieldName(collectionManager.fields, FieldUtils.DISPLAY_NAME_FIELD_NAME, false) != null}"
includeInLayout="{FieldUtils.getFieldByFieldName(collectionManager.fields, FieldUtils.DISPLAY_NAME_FIELD_NAME, false) != null}">
<s:Label text="{FieldUtils.getDisplayNameByFieldName(collectionManager.fields, FieldUtils.DISPLAY_NAME_FIELD_NAME, true)}"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"
paddingTop="5"/>
<supportClasses:FieldTextInput id="displayName" data="{hostComponent.data}" fieldName="{FieldUtils.DISPLAY_NAME_FIELD_NAME}" width="100%" fontWeight="bold" fontStyle="italic"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"/>
</s:HGroup>

<s:HGroup width="100%">
<s:Label text="{FieldUtils.getDisplayNameByFieldName(collectionManager.fields, FieldType.SIZE, true)}"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"
paddingTop="5"/>
<supportClasses:FieldTextInput id="fileSize" data="{hostComponent.data}" fieldName="{FieldType.SIZE}" width="100%"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"/>
</s:HGroup>

<s:HGroup width="100%">
<s:Label text="{FieldUtils.getDisplayNameByFieldName(collectionManager.fields, FieldType.CREATION_DATE, true)}"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"
paddingTop="5"/>
<supportClasses:FieldTextInput id="dateCreated" data="{hostComponent.data}" fieldName="{FieldType.CREATION_DATE}"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}" timeStyle="{DateTimeStyle.NONE}"/>
</s:HGroup>

<s:HGroup width="100%">
<s:Label text="{FieldUtils.getDisplayNameByFieldName(collectionManager.fields, FieldType.MOD_DATE, true)}"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"
paddingTop="5"/>
<supportClasses:FieldTextInput id="dateModified" data="{hostComponent.data}" fieldName="{FieldType.MOD_DATE}"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}" timeStyle="{DateTimeStyle.NONE}"/>
</s:HGroup>

<s:VGroup width="100%"
visible="{FieldUtils.getFieldByFieldName(collectionManager.fields, FieldUtils.TAGS_FIELD_NAME) != null}"
includeInLayout="{FieldUtils.getFieldByFieldName(collectionManager.fields, FieldUtils.TAGS_FIELD_NAME) != null}">
<s:Label text="{FieldUtils.getDisplayNameByFieldName(collectionManager.fields, FieldUtils.TAGS_FIELD_NAME, true)}" fontWeight="bold"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"/>
<supportClasses:FieldTextInput id="tags" data="{hostComponent.data}" fieldName="{FieldUtils.TAGS_FIELD_NAME}" width="100%"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"/>

<mx:Spacer height="10" />
</s:VGroup>

<s:Label text="{FieldUtils.getDisplayNameByFieldName(collectionManager.fields, FieldType.DESCRIPTION, true)}" fontWeight="bold"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"/>
<supportClasses:FieldTextArea id="description" data="{hostComponent.data}" fieldName="{FieldType.DESCRIPTION}" width="100%" height="100%"
fontSize="13" color="0x333333" filters="{whiteTxtDropShadow}"/>
</s:VGroup>

</s:Group>

</s:Group>

You can run the project to preview your work. Be sure to set the Environment to Authoring Mode and choose My Theme from the Portfolio properties tab. Your navigator should look like this.


“My Theme” Modified Visual Theme

The final step is to rename our theme and set it to be the default.

Using Custom Skins in a Theme

A “Theme” in a PDF Portfolio consists of a SWF file that contains your skin, a background image and color palette. The theme may also contain a background image as a part of the theme. You can modify these elements of a theme using the properties.xml file located in your project’s navfiles/navigator folder.

To rename your theme…

  1. Open the properties.xml file in your project’s navfiles/navigator folder.
  2. Locate line 4) and change the word gel to Meridien. This will set the active theme to “Meridien” which we will define in step 3 and make it the default when your layout is selected. Line 4 should now look like the one below.
  3. <PropertyList name="skins" labelKey="themeList" label="THEME_LIST" active="Meridien">
  4. Locate line 19 and change all occurances of myTheme or MY_THEME to Meridien. This will cause the Acrobat UI to display the word “Meridien” rather than “myTheme”. It is possible to localize these strings using the labelKey and the strings.asfx file. Line 19 should now look like the one below.
  5. <SkinBinary name="Meridien" labelKey="Meridien" label="Meridien" url="navigator/CustomTheme.swf" type="flex">
  6. Locate line 27 and change the value attribute to “0xFFFFFF”. This will set the default background color to white. Line 27 should now look like the one below.
    <SelectEffect type="colorValue" property="background" value="0xFFFFFF"/>
  7. Locate line 28 and change the value attribute to “0x4c0105″. This will set the default card header color and corresponds to the hostComponent.colorPalette.primaryColor object in any of your .mxml or ActionScript code. It should be a nice maroon.
    <SelectEffect type="colorValue" property="cardBackground" value="0x4c0105"/>
  8. Repeat steps 4 and 5 for lines 114 and 115 respectively so that your palette is applied the first time this layout is applied to a particular portfolio.

Run your project in the Preview application. Your customized theme should now be the default.


“Meridien Visual Theme

Download the completed Flash Builder 4.5 project

Download the .NAV file.

That’s the basics of skinning and themes. In editing the code for CustomCardSkin.mxml, you used data bindings to children of the “hostComponent” object; that’s either Acrobat or the Preview application. I’ll discuss how use these objects more in next article which will cover Advanced Skinning and Themes.