在TWaver Flex中实现垂直文字布局

最近有客户提到如何让Network上网元的标签垂直显示,首先想到的就是每个字符之间插入一个回车。这个用Network#labelFunction就能达到目的:

network.labelFunction = function (element:IElement):String { var name:String = element.name; if(element.getClient('vertical')) { var result:String = ''; for(var i:int=0,n:int=name.length; i<n; i++) { result += name.charAt(i) + '\n'; } result = result.substr(0, result.length-1); return result; } else { return name; } };


来段代码测试看看:

<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:twaver="http://www.servasoftware.com/2009/twaver/flex" layout="absolute" width="100%" height="100%" creationComplete="init()" backgroundColor="#FFFFFF" > <mx:Script> <![CDATA[ import twaver.Consts; import twaver.ElementBox; import twaver.IElement; import twaver.Node; import twaver.Styles; private var box:ElementBox = new ElementBox(); private function init():void { network.labelFunction = function (element:IElement):String { var name:String = element.name; if(element.getClient('vertical')) { var result:String = ''; for(var i:int=0,n:int=name.length; i<n; i++) { result += name.charAt(i) + '\n'; } result = result.substr(0, result.length-1); return result; } else { return name; } }; var node1:Node = new Node(); node1.location = new Point(100, 100); node1.setStyle(Styles.LABEL_POSITION, Consts.POSITION_LEFT_LEFT); node1.setClient('vertical', true); node1.name = '竖向文字Vertical Text'; box.add(node1); network.elementBox = box; } ]]> </mx:Script> <twaver:Network id="network" width="100%" height="100%" /> </mx:Application>


运行效果如下:
在TWaver Flex中实现垂直文字布局

上面的方法很容易让文字垂直显示,但效果不是很理想,中英文混合时,英文也被一个字母一个字母地分开了。有没有更好的方案?答案是肯定的,借助于Flex的Flash Text Engine (FTE)和Text Layout Framework (TLF),可以很容易的让文字从上到下显示。
先来看看一个小例子,设置TextLayoutFormat的blockProgression属性为BlockProgression.RL即可:

package { import flash.display.Sprite; import flashx.textLayout.container.ContainerController; import flashx.textLayout.conversion.TextConverter; import flashx.textLayout.elements.TextFlow; import flashx.textLayout.formats.BlockProgression; import flashx.textLayout.formats.TextLayoutFormat; public class StaticHelloWorld extends Sprite { public function StaticHelloWorld() { var textLayoutFormat:TextLayoutFormat = new TextLayoutFormat(); textLayoutFormat.lineHeight = 30; textLayoutFormat.locale = 'zh'; textLayoutFormat.blockProgression = BlockProgression.RL; var text:String = "测试竖向文字,再看看English如何?"; var textFlow:TextFlow = TextConverter.importToFlow(text, TextConverter.PLAIN_TEXT_FORMAT); textFlow.hostFormat = textLayoutFormat; textFlow.flowComposer.addController(new ContainerController(this, 25, 200)); textFlow.flowComposer.updateAllControllers(); } } }


运行效果如下:

在TWaver Flex中实现垂直文字布局

的确这样效果就好多了,英文不会被一个字母一个字母地打断,然后我们自定义一个Attachment:

package { import flash.display.Sprite; import flash.text.engine.FontPosture; import flash.text.engine.FontWeight; import flashx.textLayout.container.ContainerController; import flashx.textLayout.elements.ParagraphElement; import flashx.textLayout.elements.SpanElement; import flashx.textLayout.elements.TextFlow; import flashx.textLayout.formats.BlockProgression; import flashx.textLayout.formats.TextDecoration; import flashx.textLayout.formats.TextLayoutFormat; import twaver.Styles; import twaver.network.ui.BasicAttachment; import twaver.network.ui.ElementUI; public class FTELabelAttachment extends BasicAttachment { private var textLayoutFormat:TextLayoutFormat = new TextLayoutFormat(); public function FTELabelAttachment(elementUI:ElementUI, showInAttachmentCanvas:Boolean=false) { super(elementUI, showInAttachmentCanvas); this.textLayoutFormat.locale = 'zh'; this.textLayoutFormat.blockProgression = BlockProgression.RL; } override public function updateProperties():void { super.updateProperties(); this.textLayoutFormat.fontFamily = element.getStyle(Styles.LABEL_FONT); this.textLayoutFormat.color = element.getStyle(Styles.LABEL_COLOR); this.textLayoutFormat.fontSize = element.getStyle(Styles.LABEL_SIZE); this.textLayoutFormat.fontStyle = element.getStyle(Styles.LABEL_ITALIC) ? FontPosture.ITALIC : FontPosture.NORMAL; this.textLayoutFormat.fontWeight = element.getStyle(Styles.LABEL_BOLD) ? FontWeight.BOLD : FontWeight.NORMAL; this.textLayoutFormat.textDecoration = element.getStyle(Styles.LABEL_UNDERLINE ? TextDecoration.UNDERLINE : TextDecoration.NONE); var textFlow:TextFlow = new TextFlow(); textFlow.hostFormat = this.textLayoutFormat; var p:ParagraphElement = new ParagraphElement(); textFlow.addChild(p); var span:SpanElement = new SpanElement(); span.text = network.getLabel(element); p.addChild(span); var fteLabel:Sprite = new Sprite(); this.content = fteLabel; var containerController:ContainerController = new ContainerController(fteLabel, this.textLayoutFormat.fontSize, 1000); textFlow.flowComposer.addController(containerController); textFlow.flowComposer.updateAllControllers(); } override public function get position():String { return element.getStyle(Styles.LABEL_POSITION); } override public function get xOffset():Number { return element.getStyle(Styles.LABEL_XOFFSET); } override public function get yOffset():Number { return element.getStyle(Styles.LABEL_YOFFSET); } override public function get padding():Number { return element.getStyle(Styles.LABEL_PADDING); } override public function get paddingLeft():Number { return element.getStyle(Styles.LABEL_PADDING_LEFT); } override public function get paddingRight():Number { return element.getStyle(Styles.LABEL_PADDING_RIGHT); } override public function get paddingTop():Number { return element.getStyle(Styles.LABEL_PADDING_TOP); } override public function get paddingBottom():Number { return element.getStyle(Styles.LABEL_PADDING_BOTTOM); } override public function get cornerRadius():Number { return element.getStyle(Styles.LABEL_CORNER_RADIUS); } override public function get pointerLength():Number { return element.getStyle(Styles.LABEL_POINTER_LENGTH); } override public function get pointerWidth():Number { return element.getStyle(Styles.LABEL_POINTER_WIDTH); } override public function get direction():String { return element.getStyle(Styles.LABEL_DIRECTION); } override public function get fill():Boolean { return element.getStyle(Styles.LABEL_FILL); } override public function get fillColor():Number { return element.getStyle(Styles.LABEL_FILL_COLOR); } override public function get fillAlpha():Number { return element.getStyle(Styles.LABEL_FILL_ALPHA); } override public function get gradient():String { return element.getStyle(Styles.LABEL_GRADIENT); } override public function get gradientColor():Number { return element.getStyle(Styles.LABEL_GRADIENT_COLOR); } override public function get gradientAlpha():Number { return element.getStyle(Styles.LABEL_GRADIENT_ALPHA); } override public function get contentXScale():Number { return element.getStyle(Styles.LABEL_CONTENT_XSCALE); } override public function get contentYScale():Number { return element.getStyle(Styles.LABEL_CONTENT_YSCALE); } override public function get outlineWidth():Number { return element.getStyle(Styles.LABEL_OUTLINE_WIDTH); } override public function get outlineColor():Number { return element.getStyle(Styles.LABEL_OUTLINE_COLOR); } override public function get outlineAlpha():Number { return element.getStyle(Styles.LABEL_OUTLINE_ALPHA); } } }


再自定义Node和NodeUI,使用这个Attachment代替TWaver自带的LabelAttachment:
自定义Node:

package { import twaver.Node; public class FTELabelNode extends Node { public function FTELabelNode(id:Object=null) { super(id); } public override function get elementUIClass():Class { return FTELabelNodeUI; } } }


自定义NodeUI:

package { import twaver.Node; import twaver.network.Network; import twaver.network.ui.NodeUI; public class FTELabelNodeUI extends NodeUI { private var _labelAttachment:FTELabelAttachment = null; public function FTELabelNodeUI(network:Network, node:Node) { super(network, node); } override protected function checkLabelAttachment():void{ var label:String = this.network.getLabel(element); if(label != null && label != ""){ if(this._labelAttachment == null){ this._labelAttachment = new FTELabelAttachment(this, false); this.addAttachment(this._labelAttachment); } }else{ if(this._labelAttachment != null){ this.removeAttachment(this._labelAttachment); this._labelAttachment = null; } } } } }


最后,写个例子看看效果:

<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:twaver="http://www.servasoftware.com/2009/twaver/flex" layout="absolute" width="100%" height="100%" creationComplete="init()" backgroundColor="#FFFFFF" > <mx:Script> <![CDATA[ import twaver.Consts; import twaver.ElementBox; import twaver.IElement; import twaver.Node; import twaver.Styles; private var box:ElementBox = new ElementBox(); private function init():void { network.labelFunction = function (element:IElement):String { var name:String = element.name; if(element.getClient('vertical')) { var result:String = ''; for(var i:int=0,n:int=name.length; i<n; i++) { result += name.charAt(i) + '\n'; } result = result.substr(0, result.length-1); return result; } else { return name; } }; var node1:Node = new Node(); node1.location = new Point(100, 100); node1.setStyle(Styles.LABEL_POSITION, Consts.POSITION_LEFT_LEFT); node1.setClient('vertical', true); node1.name = '竖向文字Vertical Text'; box.add(node1); var node2:Node = new FTELabelNode(); node2.location = new Point(300, 100); node2.setStyle(Styles.LABEL_POSITION, Consts.POSITION_LEFT_LEFT); node2.name = '竖向文字Vertical Text'; box.add(node2); network.elementBox = box; } private function changeFontSize():void { box.forEach(function (element:IElement):void { element.setStyle(Styles.LABEL_SIZE, element.getStyle(Styles.LABEL_SIZE) + 2); }); } ]]> </mx:Script> <mx:VBox width="100%" height="100%"> <mx:HBox width="100%" height="20"> <mx:Button label="Change Font Size" click="changeFontSize()"/> </mx:HBox> <twaver:Network id="network" width="100%" height="100%" /> </mx:VBox> </mx:Application>


恩,这就是我想要的效果:

在TWaver Flex中实现垂直文字布局

更多关于FTE和TLF的信息,请参考Adobe官方文档:
TextLayoutFormat
TextFlow
Textlayout
本文完整代码见附件:FTELabelAttachment