探索 Flex 和 CSS 的强大功能

利用已经构建到 Adobe® Flex™ 框架内的 CSS 的强大功能。本文提供了在 Flex 内开始使用 CSS 所需的信息,也提供了在使用 Flex 设计和开发用户界面时加速工作流的提示和技巧。

Dan Orlando, 软件架构师, Vision Media Group

http://www.ibm.com/developerworks/i/p-dorlando.jpgDan Orlando 是一名软件架构师,擅长于客户端涉及 Flex、AIR 和 ActionScript 的富客户机-服务器应用程序。他使用过多种 Web 服务协议和服务器端语言。Dan 是 Vision Media Group 的共同创办人,还在 (RED)WIRE 团队担任过高级 UI 开发员。



2009 年 8 月 24 日

Flex 最为强大的特性之一是其包含的极大的设计灵活性。这些灵活性大部分可归功于 Adobe 用 Flex 实现 CSS。以我为例,我最近为一个新的 Adobe AIR™ 应用程序担任高级 UI 设计员,在我向全球 160,000 个用户正式发布第一个公开发布版的若干天前,我收到了大量设计更改请求。虽然很多开发人员都会将这些最后一分钟的请求看作是让人头痛的事,我却能在不足 20 分钟内完成这些更改并将其放入版本控制,而这完全得益于 Flex 和 CSS 的强大功能。

常用缩略语

  • UI:用户界面
  • W3C:World Wide Web Consortium

我将 CSS 与 Flex 的使用看作是 UI 开发内的一种利用形式。通过我在 UI 开发中积累的经验,我已经学会了为一些必然性做预期和准备。其中一个必然性是不管您在之前做了什么样的计划,项目的设计与功能需求都会在整个开发周期内随时发生变化。这是基于团队环境做大规模应用程序开发时的游戏规则,要在游戏中取胜,最好的方法就是预先判断出所有玩家的动向,然后再提前相应做好自己的定位。学会了灵活使用这个开发技巧后,您就可以立于不败之地,那时再去击打一个移动目标就变得轻松多了。Flex 的性能,比如与 CSS 的集成,恰巧可以让您这么做,正是因为这一点,我才迷上了用 Flex 进行开发。

我的目标是在您读完本文后,您也能够通过 Flex 去充分利用 CSS 的强大功能,并能作为一名精通 Flex UI 的开发者战无不胜。如果您已经是一个 Flex 爱好者,那么我希望您能通过本文学会新的通过 Flex 使用 CSS 的技巧 — 特别是对于企业级应用程序。

为何要使用 CSS?

大多数面向对象的设计模式都将设计逻辑与行为功能分离开来。由于 Adobe ActionScript™ 是一种面向对象的语言,因此它也就自然而然地要遵守这些面向对象的编程(OOP)约定。这么做的好处包括灵活性、保持应用程序易于维护、代码重用和更好的性能。

在 Web 设计界,CSS 是一种标准,用来封装组成 Web 站点的代码。考虑到 CSS 的功能和成熟性,很多有经验的 Web 设计人员都力求用 CSS 实现 Web 站点的设计和布局属性。其结果是可以获得对站点观感的更多的控制和更好的灵活性。CSS 在三、四年前就已经十分流行,那时候 Web 开发人员开始意识到如果 Web 站点的设计能够独立于该站点的行为功能,那么站点的设计在不破坏或者不会对该站点的行为代码产生负面影响的同时,可以很容易地被修改。这也推动了模板的迅速发展、对相同代码库的皮肤设置以及重新进行皮肤设置。比如,我热衷于为我的博客网站使用 WordPress。有成千上万的人都在使用相同的开源代码库来支撑其博客站点,而很多时候,您遇到的站点都是构建在 WordPress 之上的,而您却往往察觉不到这一点,这完全得益于其通过使用 CSS 对代码和设计进行的成功分离。


Flex 内的 CSS

首先,对于具有 Web 设计背景的人,最为重要的是要理解 Flex CSS 并不遵循与 W3C CSS 规定相同的约定。在 W3C CSS 版本 2.0 中被用来分离单词的连字符(-)并未用作 Flex 实现内的代码约定的一部分。相反,CSS 的 Flex 实现使用了驼峰式大小写。比如,W3C CSS2 规范内的 vertical-center 对等于 Flex CSS 内的 verticalCenter。如果您已经在使用了驼峰式大小写的编程语言内进行过编程,那么,这非常容易习惯。好的消息是 CSS 2.0 规范内可用的大部分内容在 Flex CSS 实现内也可用。并且,CSS 的 Flex 实现是在 CSS 2.0 W3C 标准上的显著扩展,提供了额外的、对 Flex 组件惟一的样式属性。


维护样式:组件与样式属性

在开始创建 Flex CSS 样式表之前,我建议您首先考虑您想要如何实现样式。出于简单性的考虑,我向您展示了四种声明样式的基本方法:

  • 通过组件的类名。 通过将组件的类名作为样式名来设置组件的样式:
    TitleWindow {
       borderColor: #f7f7f7;
       borderAlpha: 1;
       borderThicknessLeft: 0;
       borderThicknessTop: 0;
       borderThicknessBottom: 0;
       borderThicknessRight: 0;
       cornerRadius: 0;
       headerHeight: 0;
       highlightAlphas: 1, 1;
       headerColors: #f7f7f7, #f7f7f7;
       footerColors: #f7f7f7, #f7f7f7;
       backgroundColor: #f7f7f7;
       dropShadowEnabled: true;
    }
  • 通过一个惟一的样式名。通过使用一个惟一的样式名来声明样式。请确保在名字之前使用一个句点并使用驼峰式大小写约定:
    .altText
    {
    	fontFamily: TVNordEFCEOP-RegularCon;
    	fontSize: 18;
    	color: #FFFFFF;
    }
  • 通过一个组件外加一个样式名。当同一个组件需要有多种设计时(这对于具有多个视图状态的应用程序很常见),可以设置组件的样式名。这种方法还确保了只有特定的组件才可以分配到特定的样式:
    Text.bigYellowText
    {
    	color:#EFB526;
    	fontSize:36;
    	fontWeight:Bold;
    }
  • 通过全局选择器。全局选择器 是一种特殊的选择器,它能够影响包含属性集的应用程序内的每一个组件。比如,我可以将包含有 cornerRadius 样式属性的所有显示对象组件的 cornerRadius 样式属性设置为 4,如下所示:
    global
    {
    	cornerRadius: 4;	
    }

样式优先权

虽然全局选择器基本上设置的是一个属性的默认值,该值很容易被覆盖。比如,如果我在内联(inline)或在我的 CSS 文件内将 Button 组件的 cornerRadius 属性设置为 0,它将优于我已经指定的 4 这一全局默认设置;因此,所有我的 Button 组件都将包含一个值为 0cornerRadius 属性。而且,我将通过创建一个额外的样式来覆盖 4 这一全局设置0 这一 Button 设置:

Button.altCornersButton
{
	cornerRadius: 8;	
}

在 Flex 内应用样式

选择用 Flex 实现 CSS 的方法必须基于环境和条件。在考虑设计实现的可用选项时,最好是从较高的层次审视应用程序。如下的这些方法是用 Flex 实现 CSS 时最为常用的。

设置一个实例(内联)上的样式

扩展了 Flex UIComponent 基类的 Flex 组件允许作为内联属性设置常见样式 — 换言之,即在 MXML 组件声明标签内(参见 清单 1)。一个显示对象的布局属性通常对该对象惟一,所以常常能够看到某个组件被显式地设置了如下这些属性之一:xyheightwidthtoprightleftbottomhorizontalCenterverticalCenterhorizontalAlignverticalAlign

清单 1. 用 MXML 作为某个组件实例的属性设置样式
<mx:Button id="volumeIcon" cornerRadius="0" alpha="0.9"
	verticalCenter="0" enabled="true" toolTip="Volume Control"
	click="toggleVolumeControl();" />

清单 1 展示了设置样式属性的一个函数示例,这些属性对 id 值为 volumeIconButton 组件的这个特定实例是惟一的。由于我知道它是惟一一个需要这些特定样式值的按钮,因此我已经显式地设置了此特定按钮的 cornerRadiusalphaverticalCenter

设置 MXML 组件标签上的样式属性的一条经验是,只有在知道所设置的样式值特定于该组件的情况下才以这种方式设置此属性。比方说,您的应用程序要求一个容器能够垂直堆叠显示对象而不在其间留空隙。同时,您知道应用程序内的其他 VBox 容器则确实需要显示对象之间有空隙。在这种情况下,您应该在 VBox 组件的该实例上显式地设置 verticalGap 属性,如 清单 2 所示。

清单 2. 在 VBox 容器组件的这个实例上 verticalGap 被显式地设为 0
<mx:VBox id="myVBox" verticalGap="0" 
	x="15" y="15" width="100%" height="100%">
		
(...)
		
</mx:VBox>

在 MXML 内嵌入 CSS

该方法所涉及到的是用 <mx:Style/> 标签将样式或资源直接嵌入到一个 MXML 文件内。出于实用的目的,现在需要在其中直接将样式信息嵌入到 MXML 文件的实例很少。最重要的是要认识到如果应用程序包含很多被嵌入的 CSS,那么随着应用程序的增长,设计将越来越难以维护。认识到这一点,在使用这种方法时,要十分慎重,只有在需要的时候才使用它。清单 3 提供了嵌入样式声明的一个例子,该声明应该保留在一个外部 CSS 文件内以便获得更好的代码可维护性。

清单 3. 将样式直接嵌入到 MXML 应用程序文件内
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication 
	xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute"
	showFlexChrome="false"
	borderStyle="none" 
	keyUp="{this.onKeyStrokeEvent(event);}">
	
	
	<mx:Style>
		.bGroup {
			borderSides:"left,bottom,right";
			borderStyle:"solid";
			borderColor:#6d6f71;
			borderLeftThickness:3;
			borderRightThickness:3;
			borderBottomThickness:1;
			dividerColor:#6d6f71;
			dividerThickness:3;
		}
	</mx:Style>
	
	<mx:Script>
		<![CDATA[ 
			
			(...)

不过,这并不是说它将永远不会发生。清单 4 就是我基于 Flex AplicationControlBar 而为我的应用程序所构建的一个向下扩展(scaled-down)的组件。在所提供的设计内,控制栏内的每个按钮实际上都是一个简单的文本单词,看上去更像是一个链接,而非按钮。此外,所有这些单词链接之间都有一个小的 bullet 分隔符。由于我已经有了整个应用程序的设计,因此我知道这种 bullet 分隔符是应用程序主控制栏所特有的,在任何其他地方都不会出现。最为重要的是,由于 bullet 在同一个组件内出现若干次,因此非常有必要将此图像作为其自己的私有类嵌入到这个 MXML 文件内,以便我能从放置在这些链接之间的每个图像控件中绑定它。否则,结果很有可能是正在创建的同一个图像却具有多个实例,这是对系统资源的一种浪费。

清单 4. 直接嵌入一个图像
<?xml version="1.0" encoding="utf-8"?>
<mx:ApplicationControlBar xmlns:mx="http://www.adobe.com/2006/mxml">

 <mx:Script>
        <![CDATA[
        	
            [Embed(source="assets/bullet_black.png")]
            [Bindable]
            private var bullet:Class;            
            
        ]]>
    </mx:Script>

	<mx:HBox x="10" y="10" id="hbox" horizontalGap="10" width="350">
		<mx:LinkButton label="Help" styleName="appBarButton"/>
		<mx:Image source="{bullet}" />
		<mx:LinkButton label="About" styleName="appBarButton"/>
		<mx:Image source="{bullet}" />
		<mx:LinkButton label="Minimize" styleName="appBarButton" />
		<mx:Image source="{bullet}" />
		<mx:LinkButton label="Quit" styleName="appBarButton"/>
	</mx:HBox>
</mx:ApplicationControlBar>

外部样式表

一个 Flex 或 AIR 应用程序在其源代码目录的根部具有一个 MXML 文件,应用程序可以基于 Application 类(Flex)或 WindowedApplication 类(AIR)。这个文件就是默认的 MXML 应用程序文件,开始于 <mx:Application/><mx:WindowedApplication/>。应用程序的样式表源代码应该立即出现在应用程序基类声明之后(参见 清单 5)。

清单 5. 在除主应用程序 MXML 文件之外的任何文件内声明样式表都会导致错误
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication 
	xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute"
	showFlexChrome="false"
	dropShadowEnabled="false" 
	borderStyle="none" 
	applicationComplete="{this.appInit(event);}"
	>
	
	<mx:Style source="com/passalong/assets/RED_SKIN_MAIN.css"/>
	
	<mx:Script>
		<![CDATA[
			...

保持应用程序样式设置的最简便也是最安全的方法是使用外部样式表。实际上,从一个 CSS 文件就能合理保存一个大型应用程序的所有样式。我选择只使用一个 CSS 文件,出于两个原因:第一个原因是我将始终知道到哪里可以找到我的样式设置;第二个原因是在团队开发情况下,其他开发人员也可以很容易找到样式。此外,一种好的做法是很好地组织该文件,因为随着应用程序的增长,CSS 文件也必然会增长。

在构建您自己的主应用程序 CSS 文件时,通常一种好的做法是首先放置那些默认的组件样式声明。比如,若想要所有的工具提示在应用程序中都具有同样的外观,就可以使用 ToolTip 作为样式名并将其放置在文件的开始。我趋向于将 tooltip 放置在应用程序的所有内容之前,以便让我的应用程序尽量的保持用户友好性。因此,tooltip 组件在应用程序内出现的次数比其他任何组件都要多。由于默认组件样式对应用程序的整体观感具有巨大的影响,所以您需要能够快速和容易地访问到它们。这也是为什么要将这些样式放在首位的原因了。

请注意在大多数应用程序内,为每个组件创建一个默认样式并不意味着就完事大吉了。 Button 组件就是这样一个例子。同样地,要让 CSS 代码保持整齐,可以考虑对组件样式子集进行分组。比如,三个称为 Button.musicPlayerPlayButton.pageDownButton.backToMenu 的可选按钮样式将被一个挨一个地放置在样式表内并被来自其他样式组的注释所分隔。


使用 getStyle() 和 setStyle()

获取自 UIComponent 基类的所有组件继承了两种方法:getStyle()setStyle()。这两种方法为开发人员带来了难以置信的灵活性,对于通过编程实现皮肤处理来说尤其如此。虽然对此功能的用法介绍超出了本文的讨论范围,但是我还是会介绍几种不同的用法以展示此功能背后的威力。在 ActionScript 代码内使用此功能时,对 getStyle 的每个调用都需要对 setStyle 的调用,反之亦然。

清单 6 显示了当窗口进入最小化状态时如何使用这两种方法来对这些任务栏按钮进行皮肤处理。

清单 6. 当窗口最小化时,对任务栏按钮进行皮肤处理
private function toMinimized():void {
	   TaskBarManager.addToTaskBar(this);
	   pushStateProperties(stateMin, x, y, 200, titleBar.height, NaN, NaN);
	        
	   minimizeButton.setStyle("upSkin", getStyle("restoreButtonUpSkin"));
	   minimizeButton.setStyle("disabledSkin", getStyle("restoreButtonDisabledSkin"));
	   minimizeButton.setStyle("downSkin", getStyle("restoreButtonDownSkin"));
	   minimizeButton.setStyle("OverSkin", getStyle("restoreButtonOverSkin"));
	        
	   maximizeButton.setStyle("upSkin", getStyle("maximizeButtonUpSkin"));
	   maximizeButton.setStyle("disabledSkin", 
	        getStyle("maximizeButtonDisabledSkin"));
	   maximizeButton.setStyle("downSkin", getStyle("maximizeButtonDownSkin"));
	   maximizeButton.setStyle("OverSkin", getStyle("maximizeButtonOverSkin"));
	        
	   dispatchEvent(new Event("minimize"));
}

清单 6 内的 toMinimized 方法取自一个扩展了 Flex Panel 类的定制组件。这个组件给出了应用程序内新窗口的外观,用户可以最小化该新窗口以便只显示标题栏(以防用户访问其中的内容)。有关它的最酷的一点是当窗口处于最小化状态时,这个 Minimize 按钮会被 getStylesetStyle 方法动态地重新进行皮肤处理,这两种方法将此按钮转变为 Restore 按钮并为 maximize 按钮的皮肤设置新的状态。通常,我通过创建一种切换开关来重用某个类的相同实例,这节省了系统资源。

使用如下语法,通过 MXML 可以方便地使用 setStyle 方法:

<mx:SetStyle name=”styleNameAsString” value=”newStyleValue” />

setStyle 通过 MXML 被调用时,它会自动进行 getStyle 调用,所以您无需获得该样式的当前属性来重置它。正如您在 清单 7 中所见,当应用程序状态改变时,我设置了一个新的背景图像,这种方法对使用状态设计模式的应用程序尤其好用。

清单 7. 在 MXML 内调用 setStyle
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute"
	creationComplete="init()">
	
	<mx:states>
		<mx:State name="MyLibraryState">
			<mx:AddChild position="firstChild">
				<comp:CollectionsLibrary height="100%" width="100%" 
					horizontalCenter="0" verticalCenter="0">
				</comp:CollectionsLibrary>
			</mx:AddChild>
			<mx:SetStyle name="backgroundImage"
				value="@Embed(source='com/passalong/assets/
				          my_collections_background.png')" />
			<mx:SetProperty target="{vbox1}" name="y" value="442.95"/>
		</mx:State>
	</mx:states>
	(…)

运行时 CSS

Flex Version 3 内一个有趣的特性是您能像编译一个 SWF 文件一样编译一个 CSS 文件,并在运行时加载它。使用这个特性,您就能够让用户基于其各自的喜好更改应用程序的观感。您可能已经看到过这类功能在社区媒体网站内被使用过,其中 JavaScript™ 代码被用来在用户选择一个新的设计 view.Compile 样式表时切换 CSS 文件,以便在运行时加载。

毫不奇怪,在 Flex 内实现此任务十分容易。只需创建一个新的 CSS 文件,将 CSS 样式选择器放入其内,并保存此文件。然后,在 Flex 导航面板(Flex Development 模式内的左上方的面板)内,右键单击刚刚创建的这个 CSS 文件,然后单击 Compile CSS to SWF

StyleManager 类

在创建并编译完想要在应用程序内使用以便分离 SWF 文件所需的所有样式表之后,可以使用 Flex StyleManager 类来轻松切换样式表,如 清单 8 所示。

清单 8. 使用 StyleManager 类在运行时加载预编译的样式表
private function loadFirstStyleSheet():void {
	StyleManager.loadStyleDeclarations("CSS1.swf");
}
		
private function loadSecondStyleSheet():void {
	StyleManager.loadStyleDeclarations("CSS2.swf");
}

计算成本

在运行时加载预先编译的样式表时,需要考虑的很重要一点是由此给用户系统带来的负荷。使用这个设计实现方法时,应该非常谨慎和小心,过多的使用这个方法会因为缺少可用系统资源而快速降低应用程序的性能。

主题和皮肤处理

我最欣赏 Adobe 的一点就是它致力于缩小其所提供的用来简化及加快设计开发工作流程的各种工具间的差距。实际上,在设计 Flex UI 时,我喜欢的部分就是可以在 Adobe 应用程序 Flex、Illustrator®、Flash®、Photoshop® 及 Fireworks® 间自由出入 — 完全取决于我想要实现什么。如果这一点做得好,一个 “普通设计人员” 很快就能变身 “权威设计人员”。Adobe Developer Connection 有很多关于这t个题目的优秀文章(参见 参考资料)。


结束语

本文向您展示了用 Flex 应用程序实现 CSS 的一些方法和技巧。不过,我并未论及内置到 Flex 和 AIR 的设计能力 — 特别是关于创建和实现皮肤,这其中会大量涉及到 CSS 的使用。此外,尽管本文已给了您一个很好的开始,但我仍然强烈建议您在开发定制组件时要继续研究 Flex 和 CSS 的使用。这也就是说,当您完成了关于 Flex 与 CSS 的研究时且自己感觉对这个主题有了透彻的了解时,我建议您不妨尝试扩展 UIComponent 类并由此开发一个定制组件,让它成为可伸缩和可定制的,以便其他人也可以使用您的组件。

参考资料

学习

获得产品和技术

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=422556
ArticleTitle=探索 Flex 和 CSS 的强大功能
publish-date=08242009