Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Design d’une fenêtre AIR (graphisme avancé)

Par Freyskeyd (Simon Paitrault), le 11 juin 2011

Dans ce tutoriel je vais vous montrer comment réaliser un skin d’application ultra utile, qui vous ouvrir les portes de la création d’application avec un design hors du commun.
Les applications stylisées n’auront plus de secret pour vous !
Je vais déjà vous présenter le projet du tutoriel.

Le tutoriel va s’appuyer sur un design assez simple. Une interface d’application sobre, avec un logo (le mien à l’occasion) et trois boutons qui nous permettront de controler l’application avec les fonctions de réduction, de maximisation et de fermeture.

Vous trouverez ici les sources du tutoriel (partie design, prédécoupé) : Assets

Le tutoriel s’appuie sur Flex 4, certaines balises comme BorderContainer peuvent être remplacé par Canvas dans les versions précédentes de Flex.

Découverte du design

Drop Shadow et bords arrondis J’aurai pu dessiner les lignes de séparation sur Flex mais vu que dans ce tutoriel nous n’allons pas voir la partie resize par dragAndDrop… Si vous souhaitez le réaliser, la technique est la même que pour la réalisation des lignes pleine écran. (Voir plus bas)

Création de l'application

Commençons par créer une nouvelle application AIR.
Ensuite dans le XML de l'application, il faut activé le système Chrome à None

<systemChrome>none</systemChrome>

Et la transparence à true

<transparent>true</transparent>

Une fois ceci fait, il faut ajouter les paramètre suivants à la balise source de l'application (WindowedApplication)

backgroundAlpha="0" showStatusBar="false"

Ajoutons ensuite un dossier « assets » qui contiendra les différents éléments graphiques. Réaliser l'importation des fichiers avec clique droit > importer > file System

Dans le main.mxml de l’application nous allons ajouter notre image de fond à l’aide la balise Image

<s:Image id="background" source="assets/background.png" />

Si vous lancez l’application vous verrez surement que l’image est coupée, c’est à cause de la taille de l’application qui est plus petite que l’image du background. Pour cela nous allons ajouter une balise script, qui va nous permettre d’appliquer des fonctions en actionScript3 à nos éléments.
Ajoutons une fonction init() qui sera lancé quand l’application sera opérationnelle.

<fx:Script>
		<![CDATA[
			private function init():void{
			}
		]]>
</fx:Script>

N'oubliez pas d'appeller cette fonction quand l'application ce lance à l'aide de

creationComplete="init()"

à placer dans la balise principale.
Nous allons définir la taille de l'application dans la balise init()

private function init():void{
				this.height = 562;
				this.width = 762;
			}

Une fois la taille de l’application réglée nous allons placer notre balise Image dans une balise BorderContainer, pour plus de propreté. (BorderContainer est le remplaçant de Canvas dans Flex4)

<s:BorderContainer id="container" width="100%" height="100%" borderAlpha="0" backgroundAlpha="0" backgroundColor="0x212020" >
		<s:Image id="background" source="assets/background.png" />
	</s:BorderContainer>

Nous allons mettre ce borderContainer en 100% de l’application ce qui permet de couvrir la totalité de la surface de l’application. Nous paramétrons ensuite borderAlpha, backgroundAlpha à 0 pour que le borderContrainer ne soit pas visible. Nous lui passons en paramètre de couleur la couleur lié à notre image de fond.
Nous ajoutons ensuite à notre container les différents boutons de contrôle, ainsi que le logo.

<s:BorderContainer id="container" width="100%" height="100%" borderAlpha="0" backgroundAlpha="0" backgroundColor="0x212020" >
		<s:Image id="background" source="assets/background.png" />
		<s:Image id="closeID" name="close" source="assets/close_btn.png" x="{this.width-45}" y="25"/>
		<s:Image id="maxID" name="max" source="assets/maxim_btn.png" x="{this.width-65}" y="25"/>
		<s:Image id="reduceID" name="reduce" source="assets/reduce_btn.png" x="{this.width-85}" y="25"/>
		<s:Image id="logoID" name="logo" source="assets/logo.png" x="30" y="15" />
	</s:BorderContainer>

Dans la partie Script, dans la fonction init(), nous ajoutons à nos images la fonctionnalité buttonMode, qui leur permet de devenir cliquable comme un bouton. Nous ajoutons donc ensuite un écouteur pour chaque image de contrôle.

closeID.buttonMode = true;
maxID.buttonMode = true;
reduceID.buttonMode = true;
closeID.addEventListener(MouseEvent.CLICK,action);
maxID.addEventListener(MouseEvent.CLICK,action);
reduceID.addEventListener(MouseEvent.CLICK,action);

La fonction de destination va tester l’origine de l’évent par le paramètre name que nous avons défini sur chaque bouton et en fonction du résultat nous allons lancer les fonctions en rapport avec les actions voulus. Pour la partie réduction et fermeture il n’y a aucune action particulière à appliquer. Pour la partie pleine écran c’est une autre histoire.

private function action(evt:MouseEvent):void{
	switch(evt.currentTarget.name){
		case "close":
			this.close();
			break;
		case "max":
			this.maximizeApplication();
			break;
		case "reduce":
			this.minimize();
			break;
	}
}

Nous devons gérer l’aspect de l’application manuellement, en effet, notre image de fond ayant un DropShadow, celui-ci apparaitrait sur les bords de l’écran… et ça. On n’en veut pas !
On va donc passer le paramètre alpha de notre container à 1 pour le rendre visible. Nous allons aussi définir une variable de test pour savoir si l’application est en pleine écran ou non.

<fx:Script>
		<![CDATA[
			private var _isMax:Boolean;
			private function init():void{
				this.height = 562;
				this.width = 762;
				_isMax = false;
				closeID.buttonMode = true;
				maxID.buttonMode = true;
				reduceID.buttonMode = true;
				closeID.addEventListener(MouseEvent.CLICK,action);
				maxID.addEventListener(MouseEvent.CLICK,action);
				reduceID.addEventListener(MouseEvent.CLICK,action);
			}
			private function action(evt:MouseEvent):void{
				switch(evt.currentTarget.name){
					case "close":
						this.close();
						break;
					case "max":
						this.maximizeApplication();
						break;
					case "reduce":
						this.minimize();
						break;
				}
			}
			private function maximizeApplication():void{
				if(_isMax){
					this.restore();
					container.setStyle("backgroundAlpha",0);
					background.alpha = 1;
					_isMax = false;
				}else{
					_isMax = true;
					this.maximize();
					container.setStyle("backgroundAlpha",1);
					background.alpha = 0;
				}
			}
		]]>
</fx:Script>

Nous allons ensuite créer nos lignes dynamiques en as3.

<fx:Script>
		<![CDATA[
			private var _isMax:Boolean;
			private var lineDrawing:MovieClip;
			private function init():void{
				this.height = 562;
				this.width = 762;
				_isMax = false;
				closeID.buttonMode = true;
				maxID.buttonMode = true;
				reduceID.buttonMode = true;
				closeID.addEventListener(MouseEvent.CLICK,action);
				maxID.addEventListener(MouseEvent.CLICK,action);
				reduceID.addEventListener(MouseEvent.CLICK,action);
			}
			private function action(evt:MouseEvent):void{
				switch(evt.currentTarget.name){
					case "close":
						this.close();
						break;
					case "max":
						this.maximizeApplication();
						break;
					case "reduce":
						this.minimize();
						break;
				}
			}
			private function maximizeApplication():void{
				if(_isMax){
					this.restore();
					container.setStyle("backgroundAlpha",0);
					background.alpha = 1;
					_isMax = false;
				}else{
					_isMax = true;
					this.maximize();
					container.setStyle("backgroundAlpha",1);
					background.alpha = 0;
				}
			}
			private function drawingLine():void{
				if(!lineDrawing){
					lineDrawing = new MovieClip();
					this.stage.addChild(lineDrawing);
 
					lineDrawing.graphics.lineStyle(1,0x383737);
					lineDrawing.graphics.moveTo(0,60);
					lineDrawing.graphics.lineTo(Capabilities.screenResolutionX,60);
 
					lineDrawing.graphics.lineStyle(1,0x383737);
					lineDrawing.graphics.moveTo(Capabilities.screenResolutionX-100,0);
					lineDrawing.graphics.lineTo(Capabilities.screenResolutionX-100,60);
				}
			}
		]]>
</fx:Script>

Nous ajoutons un appel à la fonction lors de l'activation du maximize de l'application. Quand l'application est déjà en pleine écran et que nous demandons un restore, nous masquons le MovieClip de Line, à l'inverse, lorsque nous demandons un maximize nous affichons le MovieClip.

	<fx:Script>
		<![CDATA[
			private var _isMax:Boolean;
			private var lineDrawing:MovieClip;
			private function init():void{
				this.height = 562;
				this.width = 762;
				_isMax = false;
				closeID.buttonMode = true;
				maxID.buttonMode = true;
				reduceID.buttonMode = true;
				closeID.addEventListener(MouseEvent.CLICK,action);
				maxID.addEventListener(MouseEvent.CLICK,action);
				reduceID.addEventListener(MouseEvent.CLICK,action);
			}
			private function action(evt:MouseEvent):void{
				switch(evt.currentTarget.name){
					case "close":
						this.close();
						break;
					case "max":
						this.maximizeApplication();
						break;
					case "reduce":
						this.minimize();
						break;
				}
			}
			private function maximizeApplication():void{
				if(_isMax){
					this.restore();
					container.setStyle("backgroundAlpha",0);
					background.alpha = 1;
					lineDrawing.visible=false;
					_isMax = false;
				}else{
					drawingLine();
					_isMax = true;
					this.maximize();
					container.setStyle("backgroundAlpha",1);
					background.alpha = 0;
					lineDrawing.visible=true;
				}
			}
			private function drawingLine():void{
				if(!lineDrawing){
					lineDrawing = new MovieClip();
					this.stage.addChild(lineDrawing);
 
					lineDrawing.graphics.lineStyle(1,0x383737);
					lineDrawing.graphics.moveTo(0,60);
					lineDrawing.graphics.lineTo(Capabilities.screenResolutionX,60);
 
					lineDrawing.graphics.lineStyle(1,0x383737);
					lineDrawing.graphics.moveTo(Capabilities.screenResolutionX-100,0);
					lineDrawing.graphics.lineTo(Capabilities.screenResolutionX-100,60);
				}
			}
		]]>
</fx:Script>

Nous allons ensuite ajouter un autre borderContainer pour permettre le dragAndDrop de l’application. Nous plaçons ce borderContainer juste en dessous des boutons, pour ne pas annulé l’effet de clique sur ces boutons. Nous paramétrons le borderContainer pour qu’il n’affiche aucune donnée graphique mais qui le rendrons quand même cliquable. Nous ajoutons ensuite sur l’event MouseDown la formule startMove qui permet de bouger la fenêtre de l’application.

<s:BorderContainer id="controlBar" backgroundAlpha="0" borderAlpha="0" height="40" width="100%" x="10" mouseDown="{nativeWindow.startMove();}" />

Et voilà ! C’est fini ! Vous savez maintenant comment Skinner une application :)

Code Complet

<?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" creationComplete="init()" 
					   xmlns:mx="library://ns.adobe.com/flex/mx" backgroundAlpha="0" showStatusBar="false">
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			import mx.effects.Move;
			private var _isMax:Boolean;
			private var lineDrawing:MovieClip;
			private function init():void{
				this.height = 562;
				this.width = 762;
				_isMax = false;
				closeID.buttonMode = true;
				maxID.buttonMode = true;
				reduceID.buttonMode = true;
				closeID.addEventListener(MouseEvent.CLICK,action);
				maxID.addEventListener(MouseEvent.CLICK,action);
				reduceID.addEventListener(MouseEvent.CLICK,action);
			}
			private function action(evt:MouseEvent):void{
				switch(evt.currentTarget.name){
					case "close":
						this.close();
						break;
					case "max":
						this.maximizeApplication();
						break;
					case "reduce":
						this.minimize();
						break;
				}
			}
			private function maximizeApplication():void{
				if(_isMax){
					this.restore();
					container.setStyle("backgroundAlpha",0);
					background.alpha = 1;
					lineDrawing.visible=false;
					_isMax = false;
				}else{
					drawingLine();
					_isMax = true;
					this.maximize();
					container.setStyle("backgroundAlpha",1);
					background.alpha = 0;
					lineDrawing.visible=true;
				}
			}
			private function drawingLine():void{
				if(!lineDrawing){
					lineDrawing = new MovieClip();
					this.stage.addChild(lineDrawing);
 
					lineDrawing.graphics.lineStyle(1,0x383737);
					lineDrawing.graphics.moveTo(0,60);
					lineDrawing.graphics.lineTo(Capabilities.screenResolutionX,60);
 
					lineDrawing.graphics.lineStyle(1,0x383737);
					lineDrawing.graphics.moveTo(Capabilities.screenResolutionX-100,0);
					lineDrawing.graphics.lineTo(Capabilities.screenResolutionX-100,60);
				}
			}
		]]>
	</fx:Script>
	<s:BorderContainer id="container" width="100%" height="100%" borderAlpha="0" backgroundAlpha="0" backgroundColor="0x212020" >
		<s:Image id="background" source="assets/background.png" />
		<s:BorderContainer id="controlBar" backgroundAlpha="0" borderAlpha="0" height="40" width="100%" x="10" mouseDown="{nativeWindow.startMove();}" />
		<s:Image id="closeID" name="close" source="assets/close_btn.png" x="{this.width-45}" y="25"/>
		<s:Image id="maxID" name="max" source="assets/maxim_btn.png" x="{this.width-65}" y="25"/>
		<s:Image id="reduceID" name="reduce" source="assets/reduce_btn.png" x="{this.width-85}" y="25"/>
		<s:Image id="logoID" name="logo" source="assets/logo.png" x="30" y="15" />
	</s:BorderContainer>
 
 
</s:WindowedApplication>