[转] Flash文本引擎, 第二部分: 交互

FTE交互

在前一篇文章中, 我介绍了如何渲染TextLine, 本文将介绍如何和已经创建的TextLine交互.

TextLine是一个InteractiveObjects对象, 你可以直接增加event listener以侦听哪些交互事件。

FTE也能让你为每一个ContentElement指定EventDispatcher. 当用户和ContentElement的数据交互时, 会clone到用户指定的EventDispatcher. 我在下面的讨论中, 你会发现每种方法都有其长处和短处.

方法一: 将TextLine看作InteractiveObject

因为TextLine是InteractiveObject, 你可以监听每个TextLine实例的键盘和鼠标事件. 这种方式, 你能知道是在和哪个TextLine在交互, 但主要缺点是对其所正在渲染的ContextElement却一无所知. 一个TextLine可以渲染多个ContentElement, 多个TextLine又可以渲染同一个ContentElement.

看下面的Demo:

package
{
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.text.engine.*;
    import flash.utils.Dictionary;
    
    [SWF(width="235", height="100")]
    public class SimpleDemo2 extends Sprite
    {
        public function SimpleDemo2()
        {
            super();
            
            setup();
        }
        
        private var lineNumbers:Dictionary = new Dictionary(false);
        
        private function setup():void
        {
            addChild(lineHolder);
            lineHolder.y = 40;
            lineHolder.x = 130;
            
            var elements:Vector.<ContentElement> = new Vector.<ContentElement>();
            elements.push(
                createTextElement('Be careless ', 26),
                createTextElement('in your dress if you will, ', 16),
                createTextElement('but keep a ', 20),
                createTextElement('tidy soul.', 26),
                createTextElement('\n - Mark Twain', 20)
            );
            
            var i:int = 0;
            var block:TextBlock = new TextBlock(new GroupElement(elements));
            var line:TextLine = block.createTextLine(null, 125);
            var _y:Number = 0;
            while(line)
            {
                addChild(line);
                
                _y += line.height;
                
                line.y = _y;
                
                line.addEventListener(MouseEvent.ROLL_OVER, onMouseEvent);
                line.addEventListener(MouseEvent.ROLL_OUT, onMouseEvent);
                line.addEventListener(MouseEvent.CLICK, onMouseEvent);
                line.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
                line.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
                
                lineNumbers[line] = ++i;
                
                line = block.createTextLine(line, 125);
            }
        }
        
        private function createTextElement(text:String, size:int):TextElement
        {
            return new TextElement(text, new ElementFormat(
                new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                size)
            );
        }
        
        private function onMouseEvent(event:MouseEvent):void
        {
            var target:TextLine = TextLine(event.target);
            
            renderNotification(lineNumbers[target] + ': ' + event.type);
        }
        
        private var lineHolder:Sprite = new Sprite();
        
        private function renderNotification(text:String):void
        {
            while(lineHolder.numChildren)
                lineHolder.removeChildAt(0);
            
            var block:TextBlock = new TextBlock(
                new TextElement(text,
                                new ElementFormat(
                                new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                18)
                                )
                );
            var line:TextLine = block.createTextLine(null, 200);
            while(line)
            {
                lineHolder.addChild(line);
                
                line.y = line.height;
                
                line = block.createTextLine(line, 200);
            }
        }
        
        [Embed(source="assets/Times New Roman.ttf", embedAsCFF="true", fontFamily="Times")]
        private var times:Class;
    }
}

实际上, 有的情况, 你也没有必要知道是哪些ContentElement, 比如,  你不关心TextLine的修饰: 下划线, 删除线, 是否被选中.

下面的Demo是可以选择文字的:

package
{
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.text.engine.*;
    import flash.ui.Mouse;
    import flash.ui.MouseCursor;
    
    [SWF(width="400", height="125")]
    public class FTEDemo7 extends Sprite
    {
        private var beginIndex:int = -1;
        private var endIndex:int = -1;
        private var lines:Array = [];
        
        public function FTEDemo7()
        {
            var elements:Vector.<ContentElement> = new Vector.<ContentElement>();
            elements.push(
                createTextElement('He ', 20),
                createTextElement('who loves ', 16),
                createTextElement('practice ', 26),
                createTextElement('without ', 16),
                createTextElement('theory ', 26),
                createTextElement('is like the ', 16),
                createTextElement('sailor ', 20),
                createTextElement('who boards his ship without a ', 16),
                createTextElement('rudder ', 26),
                createTextElement('and ', 16),
                createTextElement('compass ', 26),
                createTextElement('and ', 16),
                createTextElement('never knows where he may cast.', 24),
                createTextElement('\n - Leonardo da Vinci', 22)
            );
            
            var i:int = 0;
            var block:TextBlock = new TextBlock(new GroupElement(elements));
            var line:TextLine = block.createTextLine(null, 400);
            var _y:Number = 0;
            while(line)
            {
                lines.push(addChild(line));
                _y += line.height;
                line.y = _y;
                line.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
                line.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
                line.addEventListener(MouseEvent.ROLL_OVER, onRoll);
                line.addEventListener(MouseEvent.ROLL_OUT, onRoll);
                line = block.createTextLine(line, 400);
            }
        }
        
        private function createTextElement(text:String, size:int):TextElement
        {
            return new TextElement(text, new ElementFormat(
                                   new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                   size)
                                   );
        }
        
        private function onMouseDown(event:MouseEvent):void
        {
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            graphics.clear();
            var line:TextLine = TextLine(event.target);
            beginIndex = line.textBlockBeginIndex + line.getAtomIndexAtPoint(event.stageX, event.stageY);
        }
        
        private function onMouseMove(event:MouseEvent):void
        {
            if(!event.buttonDown)
                return;
            
            var line:TextLine = TextLine(event.target);
            endIndex = line.textBlockBeginIndex + line.getAtomIndexAtPoint(event.stageX, event.stageY);
            drawBackground(beginIndex, endIndex);
        }
        
        private function onMouseUp(event:MouseEvent):void
        {
            stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            
            var objs:Array = getObjectsUnderPoint(new Point(event.stageX, event.stageY));
            var obj:Object;
            var found:Boolean = false;
            while(objs.length)
            {
                if(objs.pop() is TextLine)
                {
                    found = true;
                    break;
                }
            }
            
            Mouse.cursor = found ? MouseCursor.IBEAM : MouseCursor.ARROW;
        }
        
        private function drawBackground(begin:int, end:int):void
        {
            graphics.clear();
            
            if(begin > end)
            {
                var begin2:int = begin;
                begin = end;
                end = begin2;
            }
            
            var line:TextLine;
            for(var i:int = 0; i < lines.length; i++)
            {
                line = lines[i];
                if(line.textBlockBeginIndex <= begin && (line.textBlockBeginIndex + line.rawTextLength) >= begin)
                    break;
            }
            
            var startLine:TextLine = line;
            
            for(i = 0; i < lines.length; i++)
            {
                line = lines[i];
                if(line.textBlockBeginIndex <= end && (line.textBlockBeginIndex + line.rawTextLength) >= end)
                    break;
            }
            
            var endLine:TextLine = line;
            line = startLine;
            var bounds:Rectangle;
            
            // Don't ever do this!
            // I'm only doing this because I don't want errors 
            // and I didn't have time to vet this math 100%.
            try
            {
                while(true)
                {
                    if(line == null)
                        break;
                    
                    if(line == startLine)
                    {
                        if(startLine == endLine)
                            bounds = line.getAtomBounds(Math.min(Math.max(begin - line.textBlockBeginIndex, 0), line.rawTextLength - 1)).union(line.getAtomBounds(Math.min(Math.max(end - line.textBlockBeginIndex, 0), line.rawTextLength - 1)));
                        else
                            bounds = line.getAtomBounds(Math.min(Math.max(begin - line.textBlockBeginIndex, 0), line.rawTextLength - 1)).union(line.getAtomBounds(line.rawTextLength - 1));
                        bounds.x += line.x;
                        bounds.y += line.y;
                    }
                    else if(line == endLine)
                    {
                        bounds = line.getAtomBounds(Math.min(Math.max(end - line.textBlockBeginIndex, 0), line.rawTextLength - 1)).union(line.getAtomBounds(0));
                        bounds.x += line.x;
                        bounds.y += line.y;
                    }
                    else
                        bounds = line.getBounds(line.parent);
                    
                    graphics.beginFill(0x003399, 0.25);
                    graphics.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
                    
                    if(line == endLine)
                        break;
                    
                    line = line.nextLine;
                }
            }
            catch(e:Error)
            {
                // Do noooothing
            }
        }
        
        private function onRoll(event:MouseEvent):void
        {
            Mouse.cursor = (event.type == MouseEvent.ROLL_OVER || event.buttonDown) ? MouseCursor.IBEAM : MouseCursor.ARROW;
        }
        
        [Embed(source="assets/Times New Roman.ttf", embedAsCFF="true", fontFamily="Times")]
        private var times:Class;
    }
}

方法二: 用TextLineMirrorRegions (以面简写成TLMRs)

FTE交互的首选方式还是用 TextLineMirrorRegions, 上篇文章说过: 你必须用  TextElementGraphicElement, or GroupElement 之一来创建文本实例. 创建后你可以设置ContentElement.eventMirror属性为你所指定的EventDispatcher. 这种方式能让你和特定的ContentElement交互. 

在下面的demo代码中, 我创建了一个EventDispather对象, 并且设置给TextElement.eventMirror属性, 然后监听这个EventDispather对象的 mouseMove 事件, 每当Mouse over这个TextElement时, 就会trace下来.

var dispatcher:EventDispatcher = new EventDispatcher();
new TextElement('Inspiring quote here.', new ElementFormat(
                                         new FontDescription()), 
                                         dispatcher);
var onMouseMove:Function = function(e:MouseEvent):void{
    trace('Mouse move on ' + e.target.toString());
}
dispatcher.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);

下面Demo中的两行文字是同一个TextElement的两个不同的部分:

package
{
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.text.engine.*;
    
    [SWF(width="190", height="40")]
    public class SimpleDemo3 extends Sprite
    {
        public function SimpleDemo3()
        {
            super();
            
            addChild(lineHolder);
            lineHolder.x = 100;
            
            var dispatcher:EventDispatcher = new EventDispatcher();
            dispatcher.addEventListener(MouseEvent.MOUSE_MOVE, onMouseEvent);
            dispatcher.addEventListener(MouseEvent.MOUSE_OUT, onMouseEvent);
            dispatcher.addEventListener(MouseEvent.MOUSE_OVER, onMouseEvent);
            dispatcher.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
            dispatcher.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
            dispatcher.addEventListener(MouseEvent.CLICK, onMouseEvent);
            
            var block:TextBlock = new TextBlock(new TextElement('The quick brown fox...',
                new ElementFormat(
                    new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                    18),
                dispatcher));
            var line:TextLine = block.createTextLine(null, 100);
            var _y:Number = 0;
            while(line)
            {
                addChild(line);
                _y += line.height;
                line.y = _y;
                line = block.createTextLine(line, 100);
            }
        }
        
        private function onMouseEvent(event:MouseEvent):void
        {
            var target:TextLine = TextLine(event.target);
            
            renderNotification(event.type);
        }
        
        private var lineHolder:Sprite = new Sprite();
        
        private function renderNotification(text:String):void
        {
            while(lineHolder.numChildren)
                lineHolder.removeChildAt(0);
            
            var block:TextBlock = new TextBlock(
                new TextElement(text,
                    new ElementFormat(
                        new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                        18)
                )
            );
            var line:TextLine = block.createTextLine(null, 200);
            while(line)
            {
                lineHolder.addChild(line);
                
                line.y = line.height;
                
                line = block.createTextLine(line, 200);
            }
        }
        
        [Embed(source="assets/Times New Roman.ttf", embedAsCFF="true", fontFamily="Times")]
        private var times:Class;
    }
}

和之前的Demo有什么不同之处? TextLine有一个mirrorRegions 属性, 保存了TextLineMirrorRegion数组(Vector). 由于多个ContentElement能被同一个TextLine所渲染, TextLine会为每个ContentElement创建TLMR实例, 并且分别赋给各个ContentElement.eventMirror属性.

TextLine会监听自己的交互事件, 当事件和任何一个TLMR的事件重叠时, TextLine会通知相应的TLMR. 在所有TextLine的正常事件处理结束后. 每个TLMR会用其eventMirror属性所指的EventDispather实例再次dispatch事件一次.

这个例子中, 我为TextLine和其ContentElement的eventMirror都监听了 "MouseDown" 事件. 注意eventMirror的事件触发的时间:

package
{
    import flash.display.Sprite;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.text.engine.*;
    import flash.utils.getTimer;
    
    [SWF(width="285", height="55")]
    public class SimpleDemo4 extends Sprite
    {
        public function SimpleDemo4()
        {
            addChild(lineHolder);
            lineHolder.x = 80;
            lineHolder.y = 5;
            addChild(lineHolder2);
            lineHolder2.x = 80;
            lineHolder2.y = 25;
            
            var dispatcher:EventDispatcher = new EventDispatcher();
            dispatcher.addEventListener(MouseEvent.MOUSE_DOWN, onMouseMirrorEvent);
            
            var block:TextBlock = new TextBlock(new TextElement('Click Me.',
                new ElementFormat(
                    new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                    18),
                dispatcher));
            var line:TextLine = block.createTextLine(null, 75);
            var _y:Number = 0;
            while(line)
            {
                addChild(line);
                _y += line.height;
                line.y = _y;
                
                line.addEventListener(MouseEvent.MOUSE_DOWN, onMouseLineEvent);
                
                line = block.createTextLine(line, 75);
            }
        }
        
        private var lineHolder:Sprite = new Sprite();
        private var lineTime:Number = 0;
        private function onMouseLineEvent(event:MouseEvent):void
        {
            lineTime = getTimer();
            
            var block:TextBlock = new TextBlock(
                new TextElement(event.type + ' from line',
                    new ElementFormat(
                        new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                        14)
                )
            );
            
            while(lineHolder.numChildren)
                lineHolder.removeChildAt(0);
            
            var line:TextLine = block.createTextLine(null, 200);
            var _y:Number = 0;
            
            while(line)
            {
                lineHolder.addChild(line);
                
                _y += line.height;
                
                line.y = _y;
                
                line = block.createTextLine(line, 200);
            }
        }
        
        private var lineHolder2:Sprite = new Sprite();
        
        private function onMouseMirrorEvent(event:MouseEvent):void
        {
            var block:TextBlock = new TextBlock(
                new TextElement(event.type + ' from mirror, time between dispatches: ' + (getTimer() - lineTime) + 'ms',
                    new ElementFormat(
                        new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                        14)
                )
            );
            
            while(lineHolder2.numChildren)
                lineHolder2.removeChildAt(0);
            
            var line:TextLine = block.createTextLine(null, 200);
            var _y:Number = 0;
            
            while(line)
            {
                lineHolder2.addChild(line);
                
                _y += line.height;
                
                line.y = _y;
                
                line = block.createTextLine(line, 200);
            }
        }
        
        [Embed(source="assets/Times New Roman.ttf", embedAsCFF="true", fontFamily="Times")]
        private var times:Class;
    }
}

下面的Demo, 我用TextLineMirrorRegion为每个Element设置了不同的样式

package
{
    import flash.display.Sprite;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    import flash.text.engine.*;
    import flash.utils.Dictionary;
    
    [SWF(width="250", height="110")]
    public class FTEDemo5 extends Sprite
    {
        public function FTEDemo5()
        {
            super();
            
            // Only two things are infinite, the universe and human stupidity, and I'm not sure about the former. -Albert Einstein
            
            var e1:TextElement = new TextElement('"Only ',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 16));
            var e2:TextElement = new TextElement('two things',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.BOLD, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 24), new EventDispatcher());
            var e3:TextElement = new TextElement(' are infinite, ',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 16));
            var e4:TextElement = new TextElement('the universe',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.BOLD, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 26));
            var e5:TextElement = new TextElement(' and ',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.BOLD, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 16));
            var e6:TextElement = new TextElement('human stupidity',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.BOLD, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 26));
            var e7:TextElement = new TextElement(', and ',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 16));
            var e8:TextElement = new TextElement('I\'m not sure about the former.',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.ITALIC, FontLookup.EMBEDDED_CFF),
                                                 26), new EventDispatcher());
            var e9:TextElement = new TextElement('" -',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.ITALIC, FontLookup.EMBEDDED_CFF),
                                                 16));
            var e10:TextElement = new TextElement('Albert Einstein',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.ITALIC, FontLookup.EMBEDDED_CFF),
                                                 16), new EventDispatcher());
            
            var colors:Array = [0x00FF00, 0xFF6600, 0x0066FF];
            var mirrors:Dictionary = new Dictionary();
            var color:uint;
            var regions:Vector.<TextLineMirrorRegion>;
            var region:TextLineMirrorRegion;
            var bounds:Rectangle;
            
            var vec:Vector.<ContentElement> = new Vector.<ContentElement>();
            vec.push(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
            var block:TextBlock = new TextBlock(new GroupElement(vec));
            var line:TextLine = block.createTextLine(null, 250);
            var _y:Number = 0;
            
            while(line)
            {
                addChild(line);
                
                _y += line.height;
                
                line.y = _y;
                regions = line.mirrorRegions;
                while(regions && regions.length)
                {
                    region = regions.pop();
                    if(region.mirror in mirrors)
                        color = mirrors[region.mirror];
                    else
                        color = colors.pop();
                    
                    mirrors[region.mirror] = color;
                    
                    bounds = region.bounds;
                    graphics.beginFill(color, 0.3);
                    graphics.drawRect(bounds.x, bounds.y + line.y, bounds.width, bounds.height);
                }
                
                line = block.createTextLine(line, 250);
            }
        }
        
        [Embed(source="assets/Times New Roman.ttf", embedAsCFF="true", fontFamily="Times")]
        private var times:Class;
    }
}

注意事项:

如果没有<注意事项>, 那就不是flash player的功能了 :)

TLMR只是模拟了事件, 它不会re-dispatch它从TextLine所接受到的实例, 因为TLMR不是一个InteractiveObject.

如果你用eventMirror来监听 MouseEvent, 你要意识到那是一个伪造的事件 -- 就算是target是TextLine, 也是这样,

这些事件不是源自TextLine, 这点和player原生的事件不一样.

Rollover/rollout事件

这种模拟机制, 也意味着我们受到Adobe选择这样做的摆布(或是限制), 他们觉得没有必要模拟rollover/rollout事件. 你会发现eventMirror并没有roll相关的事件. 由于ContentElement并没有display-list children, roll event的行为就和mouseover, mouseout的行为完全一样, 相必Adobe是基于这一点认为没有必要实现roll事件的.

然而, Roll事件还是非常有必要的,

尽管 ContentElement没有 display-list children, 然而它还是有 ContentElement children的, 相当于 ContentElement的层次结构代替了 disply 的层次结构, 所以 roll 事件还是必须的.

举个例子, 看下面的xml model的渲染:

<p>
  Outside the group. 
  <group>
    <text color="#44AA00">
      First group child.
    </text>
    <text color="#AA0044">
      Second group child.
    </text>
  </group>
  Outside the group. 
</p> 

上面这种case, 你可能想让group这个node作为一个整体来看待, (就像一个带children的DisplayObjectContainer 那样).

下面就上这个xml model的例子, 你将鼠标在 First group child和 Second group child之间划动时, 你会发现你紧接着会看到来自group的 "mouseout", "mouseover"两个事件, 如果是roll 事件, 应该只会收到从child来的mouseover和mouseout, 应该没有group的相关mouseover和mouseout事件.  下面的这个demo, 按Mouse, 会清空 trace.

package
{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.Rectangle;
    import flash.text.engine.*;
    import flash.utils.Dictionary;
    
    import flashx.textLayout.formats.BaselineOffset;
    
    [SWF(width="400", height="110")]
    public class FTEDemo6 extends Sprite
    {
        public function FTEDemo6()
        {
            var e1:TextElement = new TextElement('Outside the group. ',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 18));
            
            var d2:EventDispatcher = new EventDispatcher();
            d2.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void{print("1: " + e.type);});
            d2.addEventListener(MouseEvent.MOUSE_OUT, function(e:Event):void{print("1: " + e.type);});
            var e2:TextElement = new TextElement('First group child. ',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 18, 0x44AA00), d2);
            
            var d3:EventDispatcher = new EventDispatcher();
            d3.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void{print("2: " + e.type);});
            d3.addEventListener(MouseEvent.MOUSE_OUT, function(e:Event):void{print("2: " + e.type);});
            var e3:TextElement = new TextElement('Second group child. ',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 18, 0xAA0044), d3);
            
            var e4:TextElement = new TextElement('Outside the group.',
                                                 new ElementFormat(
                                                 new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                                 18));
            
            var vec:Vector.<ContentElement> = new Vector.<ContentElement>();
            vec.push(e2, e3);
            
            var groupDispatcher:EventDispatcher = new EventDispatcher();
            groupDispatcher.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void{print("group: " + e.type);});
            groupDispatcher.addEventListener(MouseEvent.MOUSE_OUT, function(e:Event):void{print("group: " + e.type);});
            var group:GroupElement = new GroupElement(vec, null, groupDispatcher);
            
            var vec2:Vector.<ContentElement> = new Vector.<ContentElement>();
            vec2.push(e1, group, e4);
            var block:TextBlock = new TextBlock(new GroupElement(vec2));
            var line:TextLine = block.createTextLine(null, 200);
            var _y:Number = 0;
            
            while(line)
            {
                addChild(line);
                _y += line.height;
                line.y = _y;
                
                line = block.createTextLine(line, 200);
            }
            
            addChild(lineHolder);
            lineHolder.x = 200;
            
            addEventListener(MouseEvent.MOUSE_DOWN, clearLines);
        }
        
        private function clearLines(event:MouseEvent):void
        {
            while(lineHolder.numChildren)
                lineHolder.removeChildAt(0);
        }
        
        private var lineHolder:Sprite = new Sprite();
        
        private function print(str:String):void
        {
            while(lineHolder.numChildren > 6)
                lineHolder.removeChildAt(0);
            
            var block:TextBlock = new TextBlock(
                new TextElement(str,
                                new ElementFormat(
                                new FontDescription("Times", FontWeight.NORMAL, FontPosture.NORMAL, FontLookup.EMBEDDED_CFF),
                                16)
                                )
                );
            var line:TextLine = block.createTextLine(null, 200);
            while(line)
            {
                lineHolder.addChild(line);
                line = block.createTextLine(line, 200);
            }
            
            var child:DisplayObject;
            for(var i:int = 0; i < lineHolder.numChildren; i++)
            {
                child = lineHolder.getChildAt(i);
                child.y = (i+1) * child.height;
            }
        }
        
        [Embed(source="assets/Times New Roman.ttf", embedAsCFF="true", fontFamily="Times")]
        private var times:Class;
    }
}

比较:

那, 怎么搭配这两种方法呢? 简而言之, 就是: 看情况. 如果你只是需要基本的交互功能, 而并不关心上下文(比如 selection你就需要关心上下文), 那就直接在TextLine上加listener. 如果你需要关心上下文, 需要知道是在和哪个ContentElement交互, 那你只能选择 event mirroring 的方式.

P.S.  

也许我有点强迫症, 每次悬停在FTE文字上时, 我非常希望能看到 I 型的光标, 我最喜欢的Demo, 还是第二个, 因为我实现了 I 型的光标 :)  , 总之, 希望你也喜欢! 祝你好运!

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