[译] Flex 4 皮肤功能介绍

原文: http://www.adobe.com/devnet/flex/articles/flex4_skinning.html

[原创翻译链接:  http://www.smithfox.com/?e=34 转载请保留此声明]

Flex 4(代号:Gumbo)的主要主题之一是"Design in mind", 皮肤则是这个主题的重要组成部分。Flash Player是迄今为止最具创意的web工作机制。然而,Flex应用程序却有了一个不太好的名声:大部分程序看上去都很相似,那是因为许多开发人员选择了Flex默认的外观和体验(比如Halo)而不是应用丰富的样式和皮肤。

Flex 4中可以更容易地完全改变应用程序的外观和体验。新的皮肤架构建立在Flex 4的一些架构改动以及逻辑和组件视觉元素的清晰分离的基础上。正因为如此,在Flex 4的组件没有直接包含任何有关他们的视觉外观的信息。所有这些信息包含在皮肤文件中,这要感谢FXG和新的states语法,使新的皮肤文件完全可以用MXML编写,这样它们就更容易地被阅读和编写,同时也更易于工具访问。

在这篇文章中,您将了解在Flex 4对皮肤架构的改进。 通过编写一个按钮的基本皮肤,你会学到一点关于FXG和新states的语法。 接下来,您将通过制作一个slider皮肤的过程了解怎么用契约在组件和皮肤进行交互。 最后,您将通过创建一个新组件的皮肤来深入学习可变换皮肤的组件。

注:在本文档中,Halo组件是指Flex 3已经有的组件。 Spark组件指的是在Flex 4中一套新组件。


编写一个简单的按钮皮肤

FXG是利用Flash Player作矢量图形的声明标记语言。 用他你可以很容易地创建一个自定义按钮。 这个按钮,只是简单地在一个矩形框里面放些文字(见图1)。

Sample1.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:Group verticalCenter="0" horizontalCenter="0">
<s:Rect id="rect" radiusX="4" radiusY="4" top="0"
right="0" bottom="0" left="0">
<s:fill>
<s:SolidColor color="0x77CC22" />
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x131313" weight="2"/>
</s:stroke>
</s:Rect>

<s:Label text="Button!" color="0x131313"
textAlign="center" verticalAlign="middle"
horizontalCenter="0" verticalCenter="1"
left="12" right="12" top="6" bottom="6"
/>
</s:Group>
</s:Application>

The sample1 button

图1: sample1 按钮

如果你熟悉Flex 3,你一定熟悉上面的语法,虽然你可能不熟悉所使用的有些特别的组件。 Goup容器是Spark中基本的没有样式的容器。 Rect是一个新的FXG图元,没错,一个矩形。 在文档中的最后标签Lable是Spark中的新的文本组件。 读MXML时就像在描述一个组件,它是一个用1像素深灰色画出圆角的长方形,中间是一些绿色的文字。
FXG好处之一是,它不仅是让我们更容易理解绘画语句,而且因为FXG使用XML结构所以使得他可以用工具创作。 如需有关FXG信息,请参阅FXG规范


转换你的按钮图形为一个按钮皮肤

到目前为止,MXML文件还只是一个不能交互的静态的作品。 它还没采取Flex 4新的皮肤功能。 为此,你需要把它应用到Button组件并使用它作为一个皮肤。 要创建Spark皮肤文件,用Skin作为新的MXML文件的根标签。 然后,将上面的图形代码copy过来: 
ButtonSkin1.mxml

<?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" alpha.disabled=".5">

<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>

<!-- border and fill -->
<s:Rect id="rect" radiusX="4" radiusY="4" top="0" right="0" bottom="0" left="0">
<s:fill>
<s:SolidColor color="0x77CC22" />
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x131313" weight="2"/>
</s:stroke>
</s:Rect>

<!-- text -->
<s:Label text="Button!" color="0x131313"
textAlign="center" verticalAlign="middle"
horizontalCenter="0" verticalCenter="1"
left="12" right="12" top="6" bottom="6"
/>
</s:Skin>

你会发现还多了一些states。 我将稍后讨论这个。
皮肤文件完成后,你需要将它关联到一个按钮组件。 Spark架构中,每一个可变换皮肤组件是通过skinClass CSS样式来和皮肤关联起来,这个CSS样式可以用样式表设置或者直接写在MXML内。 当前例子中,稍后再使用: 
Sample2.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark">
<s:Button verticalCenter="0" horizontalCenter="0" skinClass="ButtonSkin1"
click="trace('I\'ve been clicked!')" focusIn="trace('focus...on me?')" />
</s:Application>

The sample2 button

图2: sample2 按钮

现在,您已经将一个新的皮肤文件应用到这个按钮了。 按钮组件包含所有按钮的行为逻辑。 它添加事件监听器,发送新的事件,指示组件所处state,等等。 皮肤无需处理组件中定义的所有可视元素。
但是,这个按钮现在看起来和之前创建的静态图形没有什么不同。 按钮应该是互动的,但还不是这样。这是因为你还没有定义在不同states下的按钮外观。 


介绍皮肤契约(contract)

一个静态的皮肤很无聊。 为了有点趣,皮肤必须能与组件交互,反之亦然。 这两个因素通过皮肤契约进行交互。 有三个要点是:皮肤states,data和parts(见图3)。 一方面,组件定义了这三种不同要点,另一方面,皮肤则处理这三个要点。

The skinning contract comprises data, parts, and states.

图3: 皮肤契约包含 data, parts, 和 states. 

定义皮肤states

在Spark中的每个可变换皮肤组件都有一组皮肤states。 你可以依据组件所处皮肤state来改变你的皮肤外观。对于一个按钮有四种基本皮肤states: up,over,down和disabled。 您可以为这些皮肤状态定义不同的外观(见图4)。
ButtonSkin2.mxml 

<?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" alpha.disabled=".5">

<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>

<!-- dropshadow for the down state only -->
<s:Rect radiusX="4" radiusY="4" top="0" right="0" bottom="0"
left="0" includeIn="down">
<s:fill>
<s:SolidColor color="0"/>
</s:fill>
<s:filters>
<s:DropShadowFilter knockout="true" blurX="5" blurY="5"
alpha="0.32" distance="2" />
</s:filters>
</s:Rect>

<!-- border and fill -->
<s:Rect id="rect" radiusX="4" radiusY="4" top="0" right="0"
bottom="0" left="0">
<s:fill>
<s:SolidColor color="0x77CC22" color.over="0x92D64E"
color.down="0x67A41D"/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x131313" weight="2"/>
</s:stroke>
</s:Rect>

<!-- highlight on top -->
<s:Rect radiusX="4" radiusY="4" top="2" right="2" left="2"
height="50%">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF" alpha=".5"/>
<s:GradientEntry color="0xFFFFFF" alpha=".1"/>
</s:LinearGradient>
</s:fill>
</s:Rect>

<!-- text -->
<s:Label text="Button!" color="0x131313"
textAlign="center"
verticalAlign="middle"
horizontalCenter="0" verticalCenter="1"
left="12" right="12" top="6" bottom="6"
/>
</s:Skin>

ButtonSkin2.mxml<h4>ButtonSkin2.mxml</h4>

<h4>ButtonSkin2.mxml</h4>

图4. 按钮的四个皮肤states

不同皮肤状态,组件看起来不同是因为你皮肤定义的不同。 这个皮肤文件采用了新的states语法。 这是Flex 4新功能,这使得编写state更加清晰和简洁。 语法是property.stateName="property所处状态的值值"。 例如, alpha.disabled=".5"是指当按钮进入disabled皮肤state,皮肤会改变alpha为50%。over和down状态,我定义了不同的填充色,color.over="0x92D64E" color.down="0x67A41D"。
新的state语法为MXML组件增加了includeIn和excludeFrom属性。 按钮阴影皮肤仅包含在down state,这使按钮按下时很好看。此外,为了更加地生动,所有states下我都添加了另一个矩形使按钮顶部突出。
注:更多Flex 4中语法增强信息,请查看 新的states语法规范

基于皮肤state而改变按钮外观,使得操作按钮有一种交互的体验。但你会发现,该按钮组件的文本是硬编码为"Button!"。 在下一节中,你将看到皮肤如何显示组件的数据,当前例子中,数据就是label属性。

从组件获取数据

我建议你总是把HostComponent元数据声明在你的皮肤。HostComponent元数据指向你皮肤的组件,通过它可以在皮肤中访问组件。在按钮皮肤,你可以使用这个hostComponent属性绑定到按钮label属性。 
ButtonSkin3.mxml:

<?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" alpha.disabled=".5">

<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>

...

<!-- text -->
<s:Label text="{hostComponent.label}" color="0x131313"
textAlign="center"
verticalAlign="middle"
horizontalCenter="0" verticalCenter="1"
left="12" right="12" top="6" bottom="6"
/>
</s:Skin>

当声明按钮后, 皮肤中的文字会显示label属性值.
Sample4.mxml:

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

<fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
s|Button {
skinClass: ClassReference("ButtonSkin3");
}
</fx:Style>

<s:layout>
<s:VerticalLayout />
</s:layout>
<s:Button label="Button #1" />
<s:Button label="Button #2" />
<s:Button label="Button #3" />
</s:Application>

主应用程序声明了三个按钮。 每个按钮都使用相同的皮肤文件ButtonSkin3,这是由CSS类型选择器定义的。但是,每个按钮都有不同的标签。 因为现在的皮肤拉(pulls)组件的label属性来显示文本,按钮看起来像你期盼的那样,显示不同的文字(见图5)。

The buttons now display their own labels.

图5: 按钮显示他自己的文字 

你已经看到了皮肤契约三个要点中的两个,states和data。 皮肤state是一种组件进行交互的方式,而皮肤则定义了这些states的外观和体验。数据,这些用户可设置的组件属性,通过使用HostComponent元数据和hostComponent属性能被拉到(原文:can be pulled into)皮肤中。 在上面的例子,皮肤从按钮组件中拉数据(按钮组件的label属性)。另一种方法是用皮肤parts(第三个要点)将数据推(push)到皮肤中。

继续谈皮肤契约: 皮肤parts

皮肤parts是皮肤契约的第三部分。在Spark中,每个可变换皮肤的组件都有一组皮肤parts用来帮助定义组件。 以scrollbar为例,有四个皮肤parts:增加按钮,减少按钮,轨迹带和滚动条。 再以按钮为例,他仅有一个皮肤parts,labelDisplay。这是按钮组件要求提供的一部分。在上面的按钮皮肤例子中,与其绑定文本为{hostComponent.label} ,还不如你提供一个文本组件的id labelDisplay,按钮会识别这个皮肤part,从面将label属性推送到皮肤中。
ButtonSkin4.mxml:

<?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" alpha.disabled=".5">

<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>

<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>

<!-- dropshadow for the down state only -->
<s:Rect radiusX="4" radiusY="4" top="0" right="0" bottom="0"
left="0" includeIn="down">
<s:fill>
<s:SolidColor color="0"/>
</s:fill>
<s:filters>
<s:DropShadowFilter knockout="true" blurX="5" blurY="5"
alpha="0.32" distance="2" />
</s:filters>
</s:Rect>

<!-- border and fill -->
<s:Rect id="rect" radiusX="4" radiusY="4" top="0" right="0"
bottom="0" left="0">
<s:fill>
<s:SolidColor color="0x77CC22" color.over="0x92D64E"
color.down="0x67A41D"/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x131313" weight="2"/>
</s:stroke>
</s:Rect>

<!-- highlight on top -->
<s:Rect radiusX="4" radiusY="4" top="2" right="2" left="2" height="50%">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF" alpha=".5"/>
<s:GradientEntry color="0xFFFFFF" alpha=".1"/>
</s:LinearGradient>
</s:fill>
</s:Rect>

<!-- text -->
<s:Label id="labelDisplay" color="0x131313" textAlign="center"
verticalAlign="middle"
horizontalCenter="0" verticalCenter="1"
left="12" right="12" top="6" bottom="6"
/>

<!-- transitions -->
<s:transitions>
<s:Transition>
<s:CrossFade target="{rect}" />
</s:Transition>
</s:transitions>
</s:Skin>

Label不再绑定到hostComponent。相反,我给它一个id labelDisplay ,这是一个按钮组件所需的皮肤part。按钮组件自动处理数据,将它的label属性推送到labelDisplay。
除了分配一个label皮肤part,我还在皮肤中添加了一个简单的CrossFade transition。皮肤文件是定义组件的所有可视化方面的地方,包括transition。当前例子中,随时改变按钮状态,你会看到不同state之间切换时的渐变效果。 

制作slider皮肤

皮肤parts不仅可以推送组件数据到皮肤中,组件也可以用它们来注册行为。为了讲得更明白,以slider组件为例。slider两个主要parts是轨迹条和滑动块。在这个例子中,该组件没有把任何数据推送到皮肤来显示,但它添加了事件监听器到parts中并且会根据组件的value属性执行滑动块的布局。例如,当点击轨迹条,组件会更新其value属性并且定位滑动块到适当位置。此外,还有动态皮肤parts,数据提示,这是用来拖动滑块时显示弹出的提示信息。 在图6所示是一个简单slider和一个修改后的slider。

The modified slider (left) and original slider (right).

图6: 修改后的slider(左边) 和原来的slider (右边)

为构建这个, 你的皮肤文件必须声明三个皮肤parts: thumb(滑块), track(轨迹条), and dataTip(数据提示)。
MySliderSkin.mxml

<?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"
minWidth="11" minHeight="100" alpha.disabled="0.5">
<fx:Metadata>
[HostComponent("spark.components.VSlider")]
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>

<fx:Declarations>
<fx:Component id="dataTip">
<s:DataRenderer minHeight="24" minWidth="40" x="20">
<s:Rect top="0" left="0" right="0" bottom="0">
<s:fill>
<s:SolidColor color="0xFFF46B" alpha=".9"/>
</s:fill>
<s:filters>
<s:DropShadowFilter angle="90" color="0x999999" distance="3"/>
</s:filters>
</s:Rect>

<s:Label id="labelField" text="{data}"
horizontalCenter="0" verticalCenter="1"
left="5" right="5" top="5" bottom="5"
textAlign="center" verticalAlign="middle" color="0x555555" />
</s:DataRenderer>
</fx:Component>
</fx:Declarations>

<s:Button id="track" left="5" right="5" top="0" bottom="0" skinClass="MyTrackSkin" />
<s:Button id="thumb" left="0" right="0" width="18" height="8" skinClass="MyThumbSkin" />

</s:Skin>

在皮肤中定义了这些皮肤parts这后,组件负责处理他们。它添加事件监听器到滑块,让你可以在轨迹条中拖动滑块。 它还根据相应的value值来定位滑块。请看一下上面示例代码中的MyTrackSkin和MyThumbSkin。你会看到许多FXG的例子。请注意,自定义的滑块皮肤相比默认Spark滑块皮肤有着完全不同的形状。
"数据提示"皮肤part是动态的 -- 它负责生成和布局。当前的例子中,当你拖动滑块,会在滑块右边弹出数据提示。有了皮肤契约,皮肤可以只管定义皮肤part,和所有可视化方面的内容,而不必担心有什么副作用。 所有的衔接都由组件来处理。
注:一些Flex 4内置组件不仅附加行为到皮肤parts上,同时也会推送数据到皮肤parts。另一种得到皮肤中数据的方法是通过hostComponent属性来拉他们。
当一个组件创建了皮肤时,并非所有的皮肤parts是必需的。 例如,VSlider的数据提示皮肤part并不是必需的。 如果它不存在,就不显示数据提示。 


创建可变换皮肤组件

Spark可变换皮肤组件没有在幕后做什么特别的事情。. They have data properties and advertise the skin parts and skin states they need through metadata。 他们还有少许关键的方法来用管理皮肤和皮肤parts的生命周期。您也可以和它一样轻松地创建一个新的换肤组件。
为了演示一下,你可以创建一个简单NoteCard组件,它可以用来在屏幕上显示笔记。在图7所示的例子中,应用程序随机创建多个语录。

NoteCard component instances.

图7: NoteCard 组件例子

主应用程序仅创建一个有点旋转的语录NoteCard。 有趣的部分是NoteCard类,它扩展了spark.components.supportClasses.SkinnableComponent类并且在生命周期方法中添加代码。
NoteCard.as:

package
{

[SkinState("normal")]
[SkinState("disabled")]
public class NoteCard extends SkinnableComponent
{
public function NoteCard()
{
super();
}

[SkinPart(required="true")]
public var labelDisplay:TextBase;

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

private var _text:String;

public function get text():String
{
return _text;
}

public function set text(value:String):void
{
if (_text == value)
return;
_text = value;
}

...
}
}

此组件声明数据属性,皮肤states,皮肤parts。 对于数据,NoteCard有一个公共的text属性。 此外,NoteCard有两个皮肤states,normal和disabled,用SkinStates元数据声明在类声明代码的上面。 这就告诉皮肤,它需要实现这两个states。
NoteCard还有两个皮肤parts,是通过SkinPart元数据声明的。 SkinPart元数就直接声明在皮肤part名称之上。 当前例子,labelDisplay是必需的TextBase类皮肤part,closeButton是一个可选的Button类皮肤part。
由于皮肤是运行时载入,当组件第一次启动时,你不能保证有一定有皮肤。 你也不能保证已经有了全部的皮肤parts,尤其是他们是可选的。 框架负责了这些事情: 衔接part声明和组件属性定义,并且通过皮肤生命周期方法通知组件parts已经准备好了。 


实现皮肤states

为实现皮肤states,你需重写getCurrentSkinState()方法以返回皮肤当前所处状态,当前例子中,它会返回"normal"或"disabled"。当一些事件导致皮肤state变得无效时,组件应该调用invalidateSkinState()方法。NoteCard.as

package
{
[SkinState("normal")]
[SkinState("disabled")]
public class NoteCard extends SkinnableComponent
{
...
override public function set enabled(value:Boolean) : void
{
if (enabled != value)
invalidateSkinState();
super.enabled = value;
}

override protected function getCurrentSkinState() : String
{
if (!enabled)
return "disabled";
return "normal"
}

...
}
}

当设置enabled属性时,enabled setter调用invalidateSkinState()以通知皮肤,组件的state需要改变,这样getCurrentSkinState()随即会被调用。


处理皮肤parts

处理皮肤parts,有两种主要方法应该要重写,partAdded()和partRemoved()。这些方法会告诉你一个特定的皮肤part被添加了或被删除了。当装载一个皮肤时Parts将被加入或是删除。皮肤是在运行时交换的,并且延迟加载的,所以只有在某种states情况下或者是一个动态part刚刚被创建时part才会被加入。在partAdded()方法你可以设置你想要的任何数据到part,而且也可以attach一些事件侦听到part上。当part被删除时,你应该在partRemoved()方法中做相反的事情。
NoteCard.as

package
{
public class NoteCard extends SkinnableComponent
{
[SkinPart(required="true")]
public var labelDisplay:TextBase;

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

public function set text(value:String):void
{
if (_text == value)
return;
_text = value;

if (labelDisplay)
labelDisplay.text = value;
}

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

if (instance == labelDisplay)
labelDisplay.text = _text;
if (instance == closeButton)
closeButton.addEventListener(MouseEvent.CLICK, closeButton_clickHandler);
}

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

if (instance == closeButton)
closeButton.removeEventListener(MouseEvent.CLICK, closeButton_clickHandler);
}

protected function closeButton_clickHandler(event:MouseEvent) : void
{
event.stopPropagation();

IVisualElementContainer(parent).removeElement(this);
}
}
}

在partAdded()方法中,当labelDisplay part加入时,我设置text到这个part。此外,在text属性的setter方法中,我检查,看看是否已经加入labelDisplay,如果是的话,我重新设置labelDisplay.text为组件的_text值以保证和组件text属性同步。在partAdded()方法中,我添加一个click事件监听器到closeButton皮肤part。在partRemoved()我一定要删除这个click事件监听器。
作为一个SkinnableComponent ,你需要做的就是利用这个强大的换肤机制。 当有人创造了某个组件的皮肤,他们必须实现皮肤states和皮肤parts以得到期望的组件行为。 在图6所示的皮肤在样例源代码中可以找到,即使这是一个简单的组件定义,你依然可以用不同的皮肤完全改变它的外观和体验。这就是皮肤真正的力量。
注:当创建可变换皮肤组件,您可能要决定某些行为是属于皮肤的还是组件。 没有一个明确的硬性的规则。只要能让你的工作更容易就行了。 作为一般指导,一切外观和感观的定义应在皮肤MXML文件中声明。 另一方面,如果有多个皮肤想要某个特殊行为,那么将这个行为放在组件可能是一个好主意。例如,slider中滑块的定位是做在VSlider和HSlider,没有在皮肤上。


下一步到哪里

Flex 4皮肤发生了重大修改。 明确分开了组件和皮肤。该组件包含了数据,行为和核心逻辑,而皮肤定义了组件的外观和体验。组件由ActionScript编写而皮肤写在MXML中,这是托FXG和新states语法的福。 组件和皮肤通过皮肤契约进行交互。 又因为它们是各自独立的文件,所以新的皮肤很容易应用到组件上从而完全改变他们的外观。
欲了解更多的Flex 4皮肤信息,请查看 皮肤架构规范 以及 Gumbo组件架构白皮书。 

This work is licensed under a Creative Commons Attribution-Noncommercial 3.0 Unported License.

[原创翻译链接:  http://www.smithfox.com/?e=34 转载请保留此声明]

smithfox | Sunday 21 November 2010 at 1:41 pm | | UI        | Used tags: ,

three comments

LV iPhone 6 Plus Case

In May, further rumours about the iPhone 7’s camera emerged, claiming that the new camera will be 12Mp and will indeed represent the biggest camera boost in iPhone history.

LV iPhone 6 Plus Case, (URL) - 04-06-’15 15:06
femme de menage montreal

service de femme dee ménage
Professsionnelle de l’entretien, notr dynamique entreprise
saura réaliser pourr vous l’ensemble des travaux ménagers.

G.E.M. s’engage sur une prestation de qualité ! femme de
menage montreal
Notre équipe dee spécialistes chevronnés accomplira un travail à laa fois soigné, rapide et
économique.
De plus, nous utilsons exclusivement des prolduits écologiques et hautement performants, issus des toutes dernières technologies.

La vie moderne véhicule aussi de lourdes menaces.
D’apparence inoffensive, meubles, appareils ménagers, matériaux de construction,
jouets ou objets familiers peuvent dégager des phtalates ou
autres redoutables composés chimiques.
entretien ménagerrésidentiel.G.E.M votre unique intervenant qui pourra assurer tout auu
long de l’année la continuité d’un service de haute qualitéet à la carte !

Entretien ménager résidentiel, Entretien ménager commercial, Grand ménage résidentiel et commercial, Lavage dee Vitres, Lavage de tapis, Décapage et
Cirage de planchers et Plus.

Franchise de Nettoyage Résidentiel et Commercial, service de femme de ménage ,entretien ménager commercial,
entretien ménager commercial, ménage résidentiel, service
d’entretien ménager montréal
Nouss aidons les personnes désireusesd’être propriétaires
d’entreprises prospères à devenir des professionnels du nettoyage commercial et de la
maintenance. En rejoignant notre réseau d’entreprises, vous bénéficierez
d’un avantage concurrenttiel ainsi que d’une
aide apportée par un modèle d’affaires éprouvé vous assurant de travailler avec
un leader du domaine disposant de l’expérience et la stabilité.

Vous disiez augmentez vos revennus afin d’améliorer votre quotidien ?
Faites Appel a G.E.M Ménage! service de femme de ménage ,entretien ménager commercial,entretien ménager commercial, ménage résidentiel, service
d’entretien ménager montréal, service dde femme de
ménage ,entretien ménager commercial,entretien ménager
commercial, ménage résidentiel, service d’entretien ménager montréal
Franchises de Nettoyage Résidentiel et Commercial disponible Partout A Montréal ,Rive Noord et Rive Sud.

Finanncement Sans Intérêt par G.E.M .
Un dépôt esst obligatoire

Un Investissement de 24 000$ Vous Rapporte (4800$ et plus de
Revenus parr Mois)!

Matériel et formation fourni

Nouus aidons les personnes désireuses d’être propriétaires d’entreprises prospères à devenir dees professionnels du
nettoyage commercial eet de la maintenance. En rejoignant notre réseau d’entreprises, vous bénéficierez d’un avantage concurrentiel ainsi quue d’une aide apportée
ppar un modèle d’affaires éprouvé vous assurant dde travailler aec un leader du domaine disposant de
l’expérience et la stabilité.

G.E.M Ménage vous offre l’opportunité de travailler penddant la journée de lundi a vendredi .

G.E.M Vous propose des contrats d’entretien ménager avec des horaires souples et
un revenu garanti.

Pour les personnes libres,les nouveaux arrivants ou les
sans-emplois G.E.M peut favoriser votre accès au marché du travail !

G.E.M vouus propose des contrats stables et rémunérateurs!

Dans le secteur résidentiel idéal pour un couple, ou unn homme seul
dans le secteur commercial
Vous débuter des maintenant

Des techniques et méthodes de travail éprouvées.
Une aide à la constitution ou à l’enregistrement de leur
entreprise.
Des territoires d’exercice protégés.
Entreprise clés enn main.
Un garantie qui protège votre investissement

Exigences de l’entrée de notre réseau :
Les demandes sont examinées attentivement et une
enquête relative à la candidature est menée.
Les candidats doivent passer une entrevue de sélection.
Avant tout début d’activité, les candidats doivent assister à des
sessions de formation.

Au G.E.M. nous nous assurons que nos franchisés puissent offrir à leurs clients un service professionnel d’une qualité supérieure
à celle dde la concurrence.
Franchise disponible au résidentiel et commercial

N’hésitez pas à nous contacter
469 Jean-Talon Ouest Suite # 222
Montreal QC
gemmenage.net
gemmenage.com

femme de menage montreal, (URL) - 01-09-’17 02:00
nettoyage commercial

nettoyage commercial, compagnie de menage rive sud, service de femme de ménage
Professionnelle de l’entretien, notre dynamique entreprise saura réaliser pour vous l’ensemble dess travaux ménagers.

G.E.M. s’engage suur uune prestation de qualité !

Notre équipe de spécialistes chevronnés accomplira
un travail à la fois soigné, rapide et économique.

De plus, nlus utilisons exclusivement des produits écologiques et hautement
performants, issus des toutes dernières technologies.
La vie moderne véhicule aussai de lourdes menaces. D’apparence inoffensive, meubles, appareils ménagers, matériaux de construction,
jouets ou objets familiers peuvent dégager des phtalates ou autres redoutahles composés chimiques.

G.E.M Ménage s’adapte.

Choississez le jour à votre convenance.

Le choix le plus populaire est une fois par semaine.
Toutefois, vous êtes libre de déterminer votre fréquence moindre, toutes
les deux semaines, une fois par mois…. G.E.M MÉNAGE s’adapte à votre
besoin ett à votre budget.

Nous individualisons le service de nettoyage.
Notre expérience nous a enseigné que chaque immeuble ou maison est unique,
comme les gens qui y vivent.

Le prix pour nettoyer un immeuble ou une maison est basée sur pllusieurs facteurs.
La taille du lieu et la fréquence de nettoyage sont pris en compte.
Cependant, nous intégrons également dans notre analyse, les surfaces à nettoyer et à désinfecter,
la présence ou non d’animaux de compagnie,
l’encombrement ddu lieu.

Pour vous proposer un devis qui conviendra à votre budget, G.E.M MÉNAGE vous rencontre chez vous afin de peendre
connaissance du lioeu et mieux comprendre vos besoins et
exigences dee nettoyage.Si beaucoup pensent que le nettoyage de bureaux se résume
à passer l’aspirateur, nettoyer les vitrines et vider les poubelles,
En faisant appoel à une entreprise telle que
G.E.M Québec, vous avez la garantie d’un nettoyage régulier et de qualité professionnelle.

Les prestations sont effectuées par des ayents
d’entretien qualifiés qui sont formés aux techniques de nettoyage propres à vos types de locaux.

Pour satisfairde tous vos besoins en matière d’entretien ménager, G.E.M vous offre un sewrvice d’entretien ett de nettoyage résidentiel de haute qualité à un prix parfaitement
compétitif. Tout le ménage de votre domicile est pensé
et réalisé dans ses moindres détails.

Lorsque vous remettez l’entretien dee votre maison aux mains méticuleuses dee G.E.M, rien n’est laissé au hasard.

Caque pièce est importante, et ce dans ses moindres recoins.

Non seulement nous allions notre savoir-faire à une efficacité qui dépassera
vos attentes, mais chaque entretien ménager est effectué avec la plus grande discrétion.Service d’entretien et de nettoyage résidentielune entreprise
de nettoyage industriel spécialisée dan la propreté de vos locaux professionnels (immeubles, bureaux, locaux industriels).L’équipe est composée d’agents
de nettoyage qualifiés prêts à intervenir sur tous les
types de chantiers,, après l’élaboration d’un devis de netoyage complet et précis,
qu’il s’agisse d’une intervention dde nettoyage particulier ou d’un nettoyage de locaux professionnels.De l’entretien courannt des sols, vtres et meubles – au nettoyage très spécifique de matériel professionnel ;
G,E.M prend en charge l’intégralité de votre entrwtien ménager.
Que ce soit pour vos bureaux, hôtels et surfaces de
ventes qui demandent un entretien méticuleux car représentants de votre enseigne ou espaces communs d’immeubles qui connaissent un fort taux de passage,
G.E.M MÉNAGE répond à vos besoins.
propose des prestations avec unee équipe hautenent qualifiée ainsi que du matériel
de nettoyage répondant à tous les besoinns de nettoyage dans les bureaux.Les prestations sont effectuées par des agents d’entretien qualifiés qui sont formés
aux techniques de nettoyage proprres à vos types de locaux.
Il est important de noter que les intervenants sont
employés directement par g.e.m et non par vos soins
Si beaucoup pensent que le nettoyage de bureaux se résume à passer l’aspirateur,
nettoyer les vitrines ett vider les poubelles,entretien ménager résidentiel
Franchise de Nettoyage Résidentiel et Commercial, service de femme de ménage ,entretien ménager commercial,entretien ménager commercial, ménage résidentiel,
service d’entretien ménager montréal
Nous aidons les personnes désireuses d’être propriétaires d’entreprises
prospères à devenir des professionnels du nettoyage commercial et de la maintenance.
En rejoignant notre réseau d’entreprises, vous bénéficierez d’un avantage
concurrentiel ainsi que d’une aide apportée par unn modèle
d’affaires éprouvé vous assurant de travailler avec un leader
du domaine disposant de l’expérience et la stabilité.

Vous disiez augmentez vos revenus afin d’améliorer votre quotidien ?
Faites Appel a G.E.M Ménage! service de femme de ménage ,entretien ménager commercial,entretien ménager commercial, ménage résidentiel, service d’entretien ménager montréal, service de femme de ménage ,entretien ménager commercial,entretien ménager commercial, ménage résidentiel, service d’entretien ménager montréal
Franchises dde Nettoyage Résidentiel et Commrcial
disponible Partout A Montréal ,Rive Nord et Rivve Sud.

Financement Sans Intérêt par G.E.M .
Un dépôt est oligatoire

Un Investissement de 24 000$ Vous Rapporte (4800$ et pluus dde Revenus par Mois)!

Matériel et formation fourni

Nous aidonms les personnes désireuses d’être propriétaires
d’entreprises prospères à devenir des professionnels du nettoyage commercial eet de la maintenance.
En rejoignant notr réseau d’entreprises,
vous bénéficierez d’un avantage concurrentiel ainsi que d’une aide apportée par
un modèle d’affaires éprouvé vous assurant de travailler avec un leader
du domaine disposant de l’expérience et la stabilité.

G.E.M Ménage vous offre l’opportunité dde travailler pendant la journée de lundi a vendredi .

G.E.M Vous propos des contrats d’entretien ménager avec des horaires souples et un revenu garanti.

Pour les personnes libres,les nouveaux arrivants ou less sans-emplois G.E.M peut favoriser votre accès au marché du
travail !

G.E.M vous propose des contrats stables et rémunérateurs!

Dans le secteur résidentiel idéal pour un couple, ouu
un homme seuil dans le secteur commercial
Vous débuter des mainteant

Dees techniques et méthodes de travail éprouvées.
Unee aide à la constitution oou à l’enregistrement de leur
entreprise.
Des territoires d’exercice protégés.
Entreprise clés en main.
Un garantie quii protège votre investissement

Exigences dee l’entrée de notre réseau :

Les demandes sont examinées attentivement et une enquête relative à la candidature est menée.

Les andidats doivent passer une entrevue de sélection.
Avant tout début d’activité, lles candidats dooivent assistr à ddes sessions de formation.

Au G.E.M. nous nous assurons que nos franchisés puissebt offrir à leurs cliets un service professionnel d’unequalité
supérieure à celle de la concurrence.
Franchise disponible au résidentiel et commercial

N’hésitez pas à nous contacter
469 Jean-Talon Ouest Suite # 222
Montreal QC
gemmenage.net
gemmenage.com

nettoyage commercial, (URL) - 28-09-’17 10:57
(optional field)
(optional field)
为阻止垃圾广告, 请在提交评论前, 回答一个简单问题(Please answer an simple question)
Remember personal info?
Notify
Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.