[转] Flash文本引擎, 第一部分: 概述

FTE是用来渲染文本的 "文档样式" 的, 它的主要目的是取代TextField, 而不是提供一个完整的HTML渲染引擎规模的文本布局框架. 

FTE处理一种我称之为流(flow)的东西: 格式化, 引起文本在段落内换行, 我不知道这是不是官方术语, 但似乎也挺适合. 它不处理布局相关的事, 比如项目符号,缩进排版, 图像环绕, padding等等. 它也不处理装饰之类的东西, 比如, 下划线,删除线,背景颜色,选择高亮等. FTE将这些交给外层处理, 我相信是因为:
1. 布局比流更为复杂和细致, 这些功能不需要在FlashPlayer核心中
2. 装饰不会使文本回流, 或是引起文本换行

FTE遵循了一个小的MVC架构, 大约有10个核心类, 它们提供了大部分的功能, 剩下的类是一些常量声明. 需要注意的是, 所有的FTE的类都声明为final(译者注: 不可继承)  :).

FTE Model(MVC之 model)

FTE模型的基础是ContentElement。 ContentElement的是一个抽象基类。你不能直接 new ContentElement()(这有点象DisplayObject),但可以实列化它的三个子类:TextElementGraphicElementGroupElement。这些类描述了文本树状层次结构,但在深入之前,我想先多谈一点ContentElement。

ContentElement

先看一下ContentElement的构造函数

ContentElement(elementFormat:ElementFormat = null, eventMirror:EventDispatcher = null, textRotation:String = "rotate0")

它有两个重要的参数,elementFormateventMirror(还有一个不太重要的第三个参数, 只有当你想旋转文本时才会用到这个参数)。让我们先只看ElementFormat, 后面我们再回来看eventMirror。

ElementFormat类描述了处理文本流所需的绝大部分属性。它有一个FontDescription成员,正如其名,在FontDescription中你能取到标准fontFamily、fontWeight、fontPosture(这相当于传统的Flash中的FontStyle),以及字体是如何检索自Flash播放器深度(紧凑字体格式或设备字体)的。

ElementFormat有alpha, color, baselineShift, kerning等属性,基本上都能够影响reflow(回流?)。

好的,上面描述了现在你需要知道的有关ElementFormat和FontDescription对象的内容。接下来让我们看一看你会用到的实现类。

TextElement

在这三个当中,TextElement的是最直接的。它只是接受一个文本字符串:

TextElement(text:String = null, elementFormat:ElementFormat = null, eventMirror:EventDispatcher = null, textRotation:String = "rotate0")

传入的的ElementFormat将应用于整个文本字符串。因此,如果你指定了一个红颜色的的ElementFormat,那整个文本字符串会呈现红色

GraphicElement

GraphicElement接受一个DisplayObject实例(实例!),以及它的宽度和高度:

GraphicElement(graphic:DisplayObject = null, elementWidth:Number = 15.0, elementHeight:Number = 15.0, elementFormat:ElementFormat = null, eventMirror:EventDispatcher = null, textRotation:String = "rotate0")

ElementFormat的一些属性也适用于GraphicElement,如alpha、baselineShift等。显然,GraphicElement并不遵守FontDescription和ElementFormat的字体相关设置。

GroupElement

最后是GroupElement:

GroupElement(elements:Vector.<ContentElement> = null, elementFormat:ElementFormat = null, eventMirror:EventDispatcher = null, textRotation:String = "rotate0")

GroupElement非常重要, 它是TextElements、GraphicElements或其他GroupElements任意组合的集合。 GroupElement为FTE模型提供了树的功能。 TextElement的不能拥有孩子,它只控制一个字符串。同样,GraphicElement也只是描述了一个DisplayObject实例。 而GroupElements则把它绑在一起。
GroupElements提供了一套处理标准树的API函数,你可以提取,分割,合并,组合孩子。以我的经验,你不会经常用这个,除非你正在写一个可编辑的文本字段。如果你正在编写一个可编辑的文本字段,上帝保佑你(开玩笑的,it is hella fun)。
好, 说够了模型, 下面...


FTE View(MVC之view)

有两个(两个!)类构成了FTE的视图division: TextLineTextLineMirrorRegion。现在你可以忘掉TextLineMirrorRegion,因为它是处理交互的,这是一个复杂的话题,我将在后面会详细地讨论。因此,现在只专注于TextLine。

TextLine

TextLine是一个DisplayObjectContainer。是的,这就意味着它有get/add/removeChild方法(他们仍然有效!)。TextLine也是在InteractiveObject,你可以侦听所有正常的交互事件。不过,尽管它继承自InteractiveObject,但有几个属性,是只读的。这些都已经详细地写在TextLine官方文档了。

TextLine增加了原子的概念,是指在一个TextLine不可分割的字符。单个字符以及任何图形是原子性的。重要的是要知道原子永远不能被行与行分裂。FTE测量单位最小是原子水平,没有更小。
保持原子信息可能是昂贵的, TextLine只呈现其文本,但它不知道它包含的任何原子,然而调用各种方法将引起TextLine创建原子数据。比如,你调用getAtomIndexAtPoint(), TextLine就会为每个原子创建相关信息,这样它才能计算你所指定的点是哪个原子。一旦你做完,一定要调用flushAtomData(), 这样原子数据才会GC.
TextLine有指向上一行和下一行的引用, 因为TextLine是一个双向链表! 多方便呀! 当然, 如果没有前一个引用或是后一个引用, 那自己分别就是第一个或是最后一个.
TextLine还有一个有效状态, 用来表示该行从渲染后是否已经改变。有TextLineValidity类表示这个状态。

TextLine绝对不是Sprite. TextLine是DisplayObjectContainer. 这最重要的含义是: TextLine没有graphics上下文. 这意味着你不能调用textLine.graphics.draw. :( 哎!
虽然TextLine是一个具体类, 你能直接用它, 但你不能通过调用它的构造函数来实例化它. 你需要 ...


FTE Controller(MVC之C)

FTE controller部分可能只有一个类: TextBlock。我说是"可能", 因为还有TextJustifierTabStop类, 但它们仅仅影响TextBlock的渲染, 不..., 嗯。好吧, 我说服了我自己把它们也算着控制器类, 但只有一点点.
请相信我,很快你也会认为TextLine是唯一的控制器类。
TextBlock的是一个相当标准的工厂模式的实现:TextBlock的主要工作是接受ContentElement作为输入然后输出一些你想要的给定宽度的TextLine。 ContentElement-> TextBlock -> TextLines。明白了吗?我也没有。
TextBlock 有一个函数 createTextLine():

createTextLine(previousLine:TextLine = null, width:Number = 1000000, lineOffset:Number = 0.0, fitSomething:Boolean = false):TextLine

你所要做的就是将你刚创建的前一行作为第一个参数传入, 再加一个参数表示你想让当前行有多宽, TextBlock就会为你丈量了一个新的TextLine. 你是否看到了双向链表了?

创建第一个TextLine时, 你只需要传null作为第一个参数就可以了. 如果TextBlock的content属性有内容了, 并且至少有一个原子(字符或是图形), 那么传入null总是会返回一个TextLine. 如果已经没有新行需要创建, 调用TextBlock.createTextLine() 将会返回 null.

下面的简单例子是一个宽度为200的TextBlock渲染行的过程

var y:Number = 0;
var line:TextLine = block.createTextLine(null, 200);
while(line)
{
    addChild(line);
    y += line.height;
    line.height = y;
    line = block.createTextLine(line, 200);
}

好吧, 我已经说了很多了, 该来一个例子了.

如果你看不到flash, 你就直接到 http://www.smithfox.com/myopensource/fte/SimpleDemo1.swf

下面是这个例子的代码:

package
{
  import flash.display.Sprite;
  import flash.text.engine.ContentElement;
  import flash.text.engine.ElementFormat;
  import flash.text.engine.FontDescription;
  import flash.text.engine.FontPosture;
  import flash.text.engine.FontWeight;
  import flash.text.engine.GroupElement;
  import flash.text.engine.TextBlock;
  import flash.text.engine.TextElement;
  import flash.text.engine.TextLine;
 
  [SWF(width="450", height="32")]
  public class SimpleDemo1 extends Sprite
  {
    public function SimpleDemo1()
    {
      super();
 
      var e1:TextElement = new TextElement('Consider, what makes a text line a ', new ElementFormat(new FontDescription(), 24));
      var e2:TextElement = new TextElement('text line', new ElementFormat(new FontDescription("_serif", FontWeight.NORMAL, FontPosture.ITALIC), 24));
      var e3:TextElement = new TextElement('?', new ElementFormat(new FontDescription(), 24));
 
      var e:Vector. = new Vector.();
      e.push(e1, e2, e3);
 
      var block:TextBlock = new TextBlock(new GroupElement(e));
      var line:TextLine = block.createTextLine(null, stage.stageWidth);
 
      var _y:Number = 0;
      while(line)
      {
        addChild(line);
        _y += line.height;
        line.y = _y;
        line = block.createTextLine(line, stage.stageWidth);
      }
    }
  }
}

正如你所看到, 它需要三个TextElement一个GroupElement来完成一个中间有斜体文字和不同字体文字的文本行.

demo

posted @ 2013-01-15 15:37  ddw1997  阅读(578)  评论(0编辑  收藏  举报