您的位置:首页 > 其它

Text Layout Framework 概述系列(三)核心组件

2010-07-27 15:50 288 查看
Core component(核心组件)包括 Text Layout
Framework的模型层和视图层。在这个部分我们首先分析模型层的内部数据结构以及文本和文本属性是如何存储的。接下来,我们详细地介绍框架的视图层
以及将文本组合进容器对象来呈现文本的 flow composer。

1. 文本流的层次体系
模型层用一个分层的树来表示文本。树中的每个节点都是elements包中某一个类的实例。例如,树的根节点肯定是TextFlow类的一个实例。
TextFlow类代表一个完整的文本段(an entire story of
text)。story这个词来自于PageMaker和InDesign这样的排版软件,它指的是可以作为一个整体来处理的一组文本或文本流,即使这个
文本流需要多列或者多个文本容器来显示。

除了根节点之外,剩下的节点都不是很严格的基于XHTML元素。例如,根节点只能有DivElement和ParagraphElement这两种
类型的孩子结点。DivElement和ParagraphElement与XHTML元素中的<div/>和<p/>十分相
似,但并非完全一样。DivElements的用途比XHTML元素中的<div/>更狭义,DivElements只能包含
ParagraphElements和DivElements两种节点,而XHTML中的<div/>可以包括的元素类型要多得多。幸运的
是,在导入的过程总这些区别已经被处理过了。XHTML元素中<div/>中的文本在导入时将会被转化成拥有一个SpanElement类型
节点的ParagraphElement节点,而这个SpanElement将包含这些文本。

就像DivElements,ParagraphElements也是分组元素,它不能直接包含文本或者图形。但是ParagraphElements却可以包含可以直接包含文本的四种元素类型:

SpanElement类表示一组样式一样的文本。

InLineGraphicElement类表示在文本行中可以像字符一样处理的图形元素。

LinkElement类表示超本本链接,类似XHTML中的<a/>。链接可以包括一个或多个SpanElement。

InLineGraphicElement或者TCYElement。TCYElement类表示与其他文本方向垂直的文本块,通常是一小块横向
文本嵌入到大的纵向文本中去。例如,在纵向文本中你想用一个TCYElement来表示一个小数。一个TCYElement可以包含一个或多个
SpanElement。

下面的图展示的核心组件的层次结构。理解了这些是用该框架成功编程的一个重要基础。



理解了核心组件模型(core component model)的结构对处理Text Layout
Framework的标签也是有帮助的,标签是文本的一种XML呈现方式,同样也是Text Layout
Framework的一部分。尽管该框架也支持其他的XML格式,但是框架标签是基于文本层次结构的。如果你从一个使用这种标签格式的TextFlow中
导出XML,导出的XML将完全吻合改层次结构。

你应该意识到一旦你开始使用这些定义层次节点的类来编写程序,你将会将这些类关联成一个其他类型的层次结构。如果你熟悉面向对象编程的话,你会发现
这是一个继承的层次结构。在这一章提到的类,都直接或间接的继承自一个叫FlowElement的类。这个继承的层次结构与模型的文本层次结构是正交的。
这两个交叉的层次结构某些时候会引起一些小困惑,但实际上它们是完全相互独立的。

2. 样式和流元素(FlowElement)
你可以将样式赋给文本流层次树中的流元素,不论是TextFlow的实例还是它的叶子节点(SpanElements,
InlineGraphicElements, LinkElements, and
TCYElements)。根据上下文的不同,你可以赋值的样式类型也有区别。例如,像缩进或外边距这样的样式你只能适用于段落,而像字体大小这样的样式
则适用于单个的字符,段落,甚至整个文本流。为了使用上的方便,我们将样式分为三类:容器,段落,字符。

容器类样式专门适用于整个的容器或文本。容器将在下一部分讨论,但是现在你可以简单的将容器认为是一个文本的holder(持有者?)。容器可以设
置内边距,列属性,断行,垂直对齐这样的属性。只有FlexFlow,DivElement以及实现了IContainerController接口的类
可以使用容器类的属性。所有的容器类属性都被打包进了ContainerFormat这个类中。有两个方式使用容器类的样式,如果你有许多想一次赋值的样
式,你可以使用ContainerFormat类建立一个包括所有这些样式的样式对象。然后就可以将这个样式对象赋值给任何一个
TextFlow,DivElement或者IControllerObject的实例。或者,你看你可以在这些对象上单独赋值。这三个类(接口)有一组
可以直接复制的只读的样式属性。

段落样式是在整个段落上使用的样式,并不适用于单独的字符。例如段落样式包括对齐,外边距,tab
stops(制表符?)等。你只能在ParagrphElement,DivElement以及TextFlow类的实例上使用段落样式属性。就像容器有
一个ContainerFormat类,段落也有一个ParagraphFormat类来作为段落的样式类。你可以将ParagraphFormat类的
实例赋值给任何 ParagraphElement, DivElement, 或者TextFlow的实例。同样你也可以单独赋值。

字符样式可以适用于一个或一段字符的样式。字符样式包括字体大小,字符颜色,tracking(锚定位?),字间距以及行间距等。从
CharacoerFormat类中可以找到所有字符样式的列表。你可以在任何文本流元素,这样就可以轻易地在整个段落上或文本流中应用字符样式。字符样
式同样可以像段落样式那样使用两种方式使用。你既可以创建一个CharacterFormat对象或者单独将样式属性直接赋值给FlowElement的
那些只读的对应的属性。

容器,段落和字符样式都会从文本流层次体系继承而保持一致。如果你将一个ContainerFormat的对象赋值给整个文本流,则该文本流中的每
个DisplayObjectContainerController都会继承这个对象中的那些样式值。例如你在文本流层上设置了内边距属性的值,这个设
置将应用于这个文本流中的所有容器。但是你也可以通过给一个指定的容器应用一个新的样式对象来在该容器中覆盖这些样式属性。

如果你将一个ParagraphFormat对象应用在整个文本流,则该文本流下的每个段落都会继承这个ParagraphFormat对象的样
式。例如,你将左边距和右边距属性赋值给了整个文本流,则它下面的每个段落都会继承这些样式。但是你可能说如果某一个段落是一个引用块,想和其他的段落应
用不同的样式,该怎么办。幸运的是继承的属性可以通过将一个不同的ParagraphFormat对象赋值给指定的段落而被轻易地覆盖。

字符样式属性也会以同样的方式继承。如果你在整个文本流上应用一个CharacterFormat对象,则里面的每个SpanElement,
LinkElement, InlineGraphicElement,
和TCYElement都会继承这些样式属性。例如,如果你想对整个文本流使用一个字体,既可以使用这个字体属性的CharacterFormat对象,
也可以直接将字体赋值给文本流的fontFamily属性。整个文本流的所有文本都会使用这个字体。你也可以通过直接将新的值赋给
SpanElement,LinkElement等来覆盖继承的样式,就像段落属性那样。

如果你已经将一个CharacterFormat对象赋给整个文本流,但是稍后你又决定让其中的一个段落应用不同的字符样式,你不必将段落中的每个
文本元素重新设置字符样式,你要做的仅仅是建立一个新的CharacterFormat对象然后将它应用到该段落上。该段落的所有文本将用新的字符样式呈
现,因为文本中的元素总是从离它们最近的祖先上继承样式。这个特性使得我们在文本流中控制不同的部分的样式更加得心应手。例如你可以将你的文本流分成两个
或更多的DivElements,每个都使用不同的样式对象。

当应用样式属性的时候,要记住的是:通过样式对象来赋值给文本元素将覆盖所有的样式,包括以前通过样式对象赋值或者直接用样式属性来赋值的样式。例
如,如果你将一个fontSize属性值赋值给SpanElement的fontSize属性,然后有建立了一个对象然后将它赋值给
SpanElement的characterFormat属性,SpanElement将应用SpanElement上的fontSize属性。下面是一
个这宗情况的例子,在这个例子中通过CharacterFormat对象的赋给SpanElement元素,使得其fontSize属性被设为null:

var
span:SpanElement = new
SpanElement(
)
;

span.text
= "Hello, World"
;

span.fontSize
= 48
;

var
ca:CharacterFormat = new
CharacterFormat(
)
;

ca.fontFamily
= "Helvetica"
;

span.characterFormat
= ca;


应用CharacterFormat对象会重新将fontSize属性设置为null,这意味值它的值将从祖先元素中继承。如果想保存
fontSize属性的值,可以将SpanElement的characterFormat属性作为参数传递给新的CharacterFormat的构造
函数。

var
span:SpanElement = new
SpanElement(
)
;

span.text
= "Hello, World"
;

span.fontSize
= 48
;

var
cf:CharacterFormat = new
CharacterFormat(
span.characterFormat
)
;

cf.fontFamily
= "Helvetica"
;

span.characterFormat
= cf;


3. 显示文本

现在我们已经知道了文本是如何在模型中存储的,我们开始转到文本在视图中是如何显示的。无论如何,存储在文本流层次体系中的文本必须要转换成一种
Flash Palyer可以显示的格式。Text Layout
Framework提供了两种方式,一种适合显示静态文本的简单方式和一种允许选择和编辑文本的复杂方式。在这两种情况中,文本最终都被转换成
TextLine类的实例,而TextLine正是Flash Palyer 10的flash.text.engine包的一部分。

4. 用TextLineFactory呈现

简单的方式使用TextLineFactory类,它在
flashx.textLayout.factory包中。除了简单之外,这种方式的另一个优势是它比FlowComposer的方式占用更少的内存。当
使用不需要选择或者编辑,并且不需要滚动条就能全部显示的静态文本时,我们建议使用这种方式。

TextLineFactory类有一个叫createTextLinesFromTextFlow()的静态方法,它把TextFlow的实例转
换成一系列将被显示的TextLine的实例。这个方法包括简单的三步。首先,创建一个Rectangele的实例作为文本的边界。然后,创建一个回调函
数,createTextLinesFromTextFlow()创建完每个TextLine实例后会调用这个函数。回调函数一定要把TextLine的
实例加入到Flash
Player的显示列表中。最后,调用creatTextLinesFromTextFlow()函数,包括三个参数:回调函数的引用,TextFlow
实例引用,还有用来限制边界的Rectangele的实例的引用。假设叫myFlow的TextFlow对象已经存在,下面的例子描述了这个过程:

// assumes an existing TextFlow instance named myFlow

import
flashx.textLayout
.factory
.TextLineFactory
;

import
flashx.textLayout
.elements
.TextFlow
;

import
flash.text
.engine
.TextLine
;

import
flash.geom
.Rectangle
;

// first, create a bounding rectangle

var
bounds:Rectangle = new
Rectangle(
0
,0
,300
,100
)
;

// second, create a callback function

function
callback(
txLine:TextLine)
:void
{
addChild(
txLine)
; }

// third, call createTextLinesFromTextFlow()

TextLineFactory.createTextLinesFromTextFlow
(
callback, myFlow, bounds)
;


5. 用Flow Composer呈现:

也许简单的方式对你来说已经够用了,但是如果你想对文本的显示拥有更多的控制,或者你想让你的用户可以选择和编辑文本,你需要使用一个叫flow
composer的东西。一个flow
composer是一个StandardFlowComposer类的实例,这个类在flashx.textLayout.compose包中。它不仅可
以将TextFlow对象转换成TextLine对象,并且将这些TextLine对象放置进一个或多个容器中。



每个TextFlow的实例对应一个实现了IFlowComposer接口的对象。这个IFlowComposer对象可以通过
TextFlow.flowComposer属性访问。你可以通过这个属性调用IFlowComposer接口定义的方法,这些方法允许你将文本与一个或
多个容器关联,以及为文本在容器中显示做准备。

目前在框架中只有一个类实现了IFlowComposer类,StandardFlowComposer。这意味着
TextFlow.flowComposer属性,如果不是null则一定是指向了一个StandardFlowComposer对象。以后也许会有很多
新的实现IFlowComposer的类被加入到框架中。

一个容器就是一个Sprite类的实例,Sprite类是DisplayObjectContainer的子类。这两个类都是Flash
Player显示列表API的一部分。如果你对Flash
Palyer显示列表不太熟悉,你可以认为DisplayObjectContainer是一个存放Flash Player可以呈现的对象的容器。

你可以把容器当做是一个在显示文本流的简单方式中的方形边框(bounding Rectangle)的一个更高级的形式。就像bounding
Rectangle,容器划定了TextLine对象显示的边界。与之不同的是,容器可以通过滚动条滚动来让用户看到超过容器边界的文本。容器的另一个优
势是可以以FlowElement同样的方式应用样式。你可以创建一个ContainerFormat类然后将它应用到一个容器。而且,容器可以处理事
件,这样就可以和用户交互。

所有这些加强的功能是以额外的复杂度为代价的。像样式对象(formatting object)和滚动选项(scrolling
optioins)这样的额外属性需要被管理和储存。框架通过将每个容器包装进一个控制器对象来处理这些,控制器对象不仅包含这些容器,而且包含一些和样
式,滚动和其他容器属性相关的属性。一个控制器对象是一个在flashx.textLayout.container包中的
DisplayObjectContainerController类的实例。这些叫“container
controllers”的控制器对象,直接由flow composer管理。

当你的TextFlow准备好在屏幕上显示时,用flow composer创建一个controller对象并与flow
composer关联。一旦已有了关联的容器,你必须在显示之前组合文本。相应的,你可以认为容器显示周期有两个阶段:组合和显示。组合文本时将文本从文
本流层次结构转换成TextLine实例并且计算这些文本如何适应到容器的过程。显示文本涉及到通过调用flow
composer的updateAllContainers方法来更新Flash Player的显示列表。

例如,下面的代码创建了一个叫myFlow的文本流实例和一个控制器对象来显示它。一旦控制器对象被建立了,它必须要与TextFlow的flowComposer属相关联。最后,调用updateAllContainers方法将文本组合,显示列表将被更新。

// import necessary classes

import
flashx.textLayout
.container
.*
;

import
flashx.textLayout
.compose
.*
;

import
flashx.textLayout
.elements
.TextFlow
;

import
flashx.textLayout
.conversion
.TextFilter
;

// first, create a TextFlow instance named myFlow

var
markup:XML
= <
TextFlow><
p><
span>
Hello, World</
span></
p></
TextFlow>
;

var
textFlow:TextFlow = TextFilter.importToFlow
(
markup, TextFilter.TEXT_LAYOUT_FORMAT
)
;

// second, create a controller

// the first parameter, this, must point to a DisplayObjectContainer

// the last two parameters set the initial size of the container in pixels

var
contr:IContainerController = new
DisplayObjectContainerController(
this
, 600
, 600
)
;

// third, associate it with the flowComposer property

myFlow.flowComposer
.addController
(
contr)
;

// fourth, update the display list

myFlow.flowComposer
.updateAllContainers
(
)
;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: