Tuesday, April 17, 2012

KirSizer - Flex AIR Image Sizer app: Part 1

In this tutorial we will start making a Flex AIR image resizing application - KirSizer.

KirSizer is an image resizer that can resize batches of files in several ways, rename the output files using a customizable pattern, save them in a specified destination or overwrite the existing files in the existing folder, optimize pictures, keep their initial format or convert them all to JPG or PNG formats.

I am using FlashDevelop 4.0.0 to create this Flex 4 AIR application, it is free and I recommend you use it too if you want to keep up with the tutorials. It is recommended, but not necessary, though.

First, create a new Project by going to Project > New project > AIR Flex 4 Projector. Give your project a name, I named it KirSizer. Save it somewhere.

The results are 4 directories - bat, bin, lib, src and 4 files outside of these folders - AIR_readme.txt, application.xml, PackageApp.bat, Run.bat. The Main mxml file is located in the src directory.

Let's go there and start scripting.

Firstly, we set width and height to 300x460 and set showStatusBar to false in the root tags.

<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460" 
showStatusBar="false" title="KirSizer">

Go to the application.xml file and set "resizable" to false.

Now, in the Main file, let's create a ViewStack container called contentStack, which contains 2 NavigatorContents. The NavigatorContents are containers for 2 "pages" of the application - the first one is where the files are imported, the second one is where the options are set. The third one will be added in the future, and it will be the screen that's shown during the resizing process.

The first NavigatorContent contains a VGroup, which contains a Label, which shows how many files are selected, a rather big TileList object, which will contain the images that are selected, 3 buttons - "Add folder", "Add files" and "Continue". The third button has a click event handler that sets contentStack's selectedIndex to 1 (goes to the next page).

The first NavigatorContent object so far:

<s:NavigatorContent width="100%" height="100%">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Label>0 files selected</s:Label>
<mx:TileList id="tileList" width="100%" height="100%"/>
<s:Button label="Add folder" width="100%" />
<s:Button label="Add files" width="100%"/>
<s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" />
</s:VGroup>
</s:NavigatorContent>

The second NavigatorContent object contains the options for the resize project.

Inside of it, add a VGroup container, and now let's start filling it with content.

The first object inside of it will be a Button "Return to file selection", which sets contentStack's selectedIndex to 0.

<s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;"/>

Then we create an "actionCombo" ComboBox object, which the user will use to choose what he wants to do with the selected files. Set its dataProvider to "actions" ArrayCollection, which we will declare later in the declarations tags:

<s:Label>Resize options:</s:Label>

<mx:ComboBox width="100%" id="actionCombo" dataProvider="{actions}" selectedIndex="0" editable="false" />

After that we create 2 HGroups containing several objects for width and height options. Each HGroup contains a label, a NumericStepper and a ComboBox. Set the ids of NumericSteppers to newWidth and newHeight, minimum values to 1 and default values to 100. Set the ids of ComboBox objects to widthMeasure and heightMeasure, their dataProviders bound to "measures" ArrayCollection. The "measuers" ArrayCollection will consist of 2 String values - "%" and "px", that let the user choose between pixels and percentages.

We need to set an if statement in the maximum property of both NumericSteppers - check if the ComboBox's selectedIndex is 0 or 1. If the percentage is selected, set maximum to 100, otherwise - to 4000.

<s:HGroup verticalAlign="middle">
<s:Label>Width:</s:Label>
<s:NumericStepper id="newWidth" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" />
<mx:ComboBox id="widthMeasure" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" />
</s:HGroup>

<s:HGroup verticalAlign="middle">
<s:Label>Height:</s:Label>
<s:NumericStepper id="newHeight" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/>
<mx:ComboBox id="heightMeasure" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" />
</s:HGroup>

Next, we'll add an option to set output file names. It consists of an HGroup with a TextInput and a Button. Set the default value of the TextInput to %initialName%. We will later use this expression (and not only this) to let the user create his own names. This will be explained in later tutorials. The user will need help and explanation on this feature, so we add a help button.

<s:Label>Output file names:</s:Label>
<s:HGroup verticalAlign="middle">
<mx:TextInput width="240" text="%initialName%" />
<s:Button width="35" label="?"/>
</s:HGroup>

Next we'll add an output destination option. Here we create 2 RadioButtons - "Same directory" and "Specified directory". Depending on which radio button is checked, we enable or disable the TextInput and Button objects that are below, and which are used to browse for a directory.

<s:Label>Output destination:</s:Label>
<s:HGroup verticalAlign="middle">
<s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" />
<s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" />
</s:HGroup>
<s:HGroup verticalAlign="middle" width="100%">
<mx:TextInput width="100%" enabled="{newDestination.selected}" text="Select destination..." editable="false" />
<s:Button width="80" label="Browse" enabled="{newDestination.selected}"/>
</s:HGroup>

Then we add an output format option, consisting of a ComboBox that is bound to the "formats" ArrayCollection:

<s:Label>Output format:</s:Label>
<mx:ComboBox width="100%" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" />

If the outputted format is JPG, we let the user choose quality for the pictures:

<s:Label>Output JPG quality:</s:Label>
<s:HSlider width="100%" minimum="1" maximum="100" value="100" />

Finally, we add a "Resize" button:

<s:Button label="Resize" width="100%" />

I mentioned 3 ArrayCollection objects above - measures, actions and formats. The measures one contains 2 string objects - "%" and "px". The actions collection contains possible resizing actions that the user is able to perform. I added 4 items to this collection with explanations of what each option does. The third array collecion, formats, consists of 3 items - the first one keeps the initial format on output files, the second one converts everything to JPG pictures, and the third one converts them to PNGs.

<fx:Declarations>
<mx:ArrayCollection id="measures">
<fx:String>%</fx:String>
<fx:String>px</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="actions">
<fx:String>Fixed width, fixed height</fx:String>
<fx:String>Fixed width, proportional height</fx:String>
<fx:String>Proportional width, fixed height</fx:String>
<fx:String>Proportional sizes to fit specified sizes</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="formats">
<fx:String>Same format as initial file</fx:String>
<fx:String>Convert all to JPG</fx:String>
<fx:String>Convert all to PNG</fx:String>
</mx:ArrayCollection>
</fx:Declarations>

Full code so far:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460" 
showStatusBar="false" title="KirSizer">
   
<fx:Declarations>
<mx:ArrayCollection id="measures">
<fx:String>%</fx:String>
<fx:String>px</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="actions">
<fx:String>Fixed width, fixed height</fx:String>
<fx:String>Fixed width, proportional height</fx:String>
<fx:String>Proportional width, fixed height</fx:String>
<fx:String>Proportional sizes to fit specified sizes</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="formats">
<fx:String>Same format as initial file</fx:String>
<fx:String>Convert all to JPG</fx:String>
<fx:String>Convert all to PNG</fx:String>
</mx:ArrayCollection>
</fx:Declarations>
   
<mx:ViewStack id="contentStack" width="100%" height="100%">
<s:NavigatorContent width="100%" height="100%">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Label>0 files selected</s:Label>
<mx:TileList id="tileList" width="100%" height="100%"/>
<s:Button label="Add folder" width="100%" />
<s:Button label="Add files" width="100%"/>
<s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent width="100%" height="100%">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;"/>

<s:Label>Resize options:</s:Label>

<mx:ComboBox width="100%" id="actionCombo" dataProvider="{actions}" selectedIndex="0" editable="false" />
<s:HGroup verticalAlign="middle">
<s:Label>Width:</s:Label>
<s:NumericStepper id="newWidth" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" />
<mx:ComboBox id="widthMeasure" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" />
</s:HGroup>

<s:HGroup verticalAlign="middle">
<s:Label>Height:</s:Label>
<s:NumericStepper id="newHeight" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/>
<mx:ComboBox id="heightMeasure" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" />
</s:HGroup>

<s:Label/>

<s:Label>Output file names:</s:Label>
<s:HGroup verticalAlign="middle">
<mx:TextInput width="240" text="%initialName%" />
<s:Button width="35" label="?"/>
</s:HGroup>

<s:Label/>

<s:Label>Output destination:</s:Label>
<s:HGroup verticalAlign="middle">
<s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" />
<s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" />
</s:HGroup>
<s:HGroup verticalAlign="middle" width="100%">
<mx:TextInput width="100%" enabled="{newDestination.selected}" text="Select destination..." editable="false" />
<s:Button width="80" label="Browse" enabled="{newDestination.selected}"/>
</s:HGroup>

<s:Label/>

<s:Label>Output format:</s:Label>
<mx:ComboBox width="100%" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" />

<s:Label/>

<s:Label>Output JPG quality:</s:Label>
<s:HSlider width="100%" minimum="1" maximum="100" value="100" />

<s:Label/>

<s:Button label="Resize" width="100%" />
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</s:WindowedApplication>

Thanks for reading!

No comments:

Post a Comment