TextField 示例:报纸风格的文本格式设置Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本 “新闻布局”示例设置文本的格式,使文本的外观看起来有点象印刷报纸中的素材。输入文本可以包含标题、副标题和素材正文。在给定显示宽度和高度的情况下,此“新闻布局”示例将会设置标题和副标题的格式,使其占据整个显示区域的宽度。素材文本分布在两列或更多列中。 此示例演示以下 ActionScript 编程技巧:
若要获取此示例的应用程序文件,请参阅 www.adobe.com/go/learn_programmingAS3samples_flash_cn。“新闻布局”应用程序文件位于 Samples/NewsLayout 文件夹中。该应用程序包含以下文件:
读取外部 CSS 文件“新闻布局”应用程序开始时读取本地 XML 文件中的素材文本。然后,它读取提供标题、副标题和主体文本的格式设置信息的外部 CSS 文件。 CSS 文件定义三种样式:用于素材的标准段落样式和分别用于标题和副标题的 h1 和 h2 样式。 p { font-family: Georgia, "Times New Roman", Times, _serif; font-size: 12; leading: 2; text-align: justify; indent: 24; } h1 { font-family: Verdana, Arial, Helvetica, _sans; font-size: 20; font-weight: bold; color: #000099; text-align: left; } h2 { font-family: Verdana, Arial, Helvetica, _sans; font-size: 16; font-weight: normal; text-align: left; } 用于读取外部 CSS 文件的方法与加载外部 CSS 文件中所述的方法相同。加载 CSS 文件后,应用程序执行 onCSSFileLoaded() 方法,如下所示。 public function onCSSFileLoaded(event:Event):void { this.sheet = new StyleSheet(); this.sheet.parseCSS(loader.data); h1Format = getTextStyle("h1", this.sheet); if (h1Format == null) { h1Format = getDefaultHeadFormat(); } h2Format = getTextStyle("h2", this.sheet); if (h2Format == null) { h2Format = getDefaultHeadFormat(); h2Format.size = 16; } pFormat = getTextStyle("p", this.sheet); if (pFormat == null) { pFormat = getDefaultTextFormat(); pFormat.size = 12; } displayText(); } onCSSFileLoaded() 方法创建一个 StyleSheet 对象并使之分析输入 CSS 数据。素材的主体文本将显示在 MultiColumnTextField 对象中,该对象可以直接使用 StyleSheet 对象。不过,标题字段使用 HeadlineTextField 类,该类使用 TextFormat 对象进行格式设置。 onCSSFileLoaded() 方法调用两次 getTextStyle() 方法,将 CSS 样式声明转换为 TextFormat 对象,以便与两个 HeadlineTextField 对象中的每个对象配合使用。 public function getTextStyle(styleName:String, ss:StyleSheet):TextFormat { var format:TextFormat = null; var style:Object = ss.getStyle(styleName); if (style != null) { var colorStr:String = style.color; if (colorStr != null && colorStr.indexOf("#") == 0) { style.color = colorStr.substr(1); } format = new TextFormat(style.fontFamily, style.fontSize, style.color, (style.fontWeight == "bold"), (style.fontStyle == "italic"), (style.textDecoration == "underline"), style.url, style.target, style.textAlign, style.marginLeft, style.marginRight, style.indent, style.leading); if (style.hasOwnProperty("letterSpacing")) { format.letterSpacing = style.letterSpacing; } } return format; } CSS 样式声明和 TextFormat 对象的属性名称和属性值的含义不同。getTextStyle() 方法可以将 CSS 属性值转换为 TextFormat 对象预期的值。 在页面上排列素材元素StoryLayout 类可以设置标题、副标题和主体文本字段的格式并将这些字段的布局排列为报纸样式。displayText() 方法一开始会创建并放置各个字段。 public function displayText():void { headlineTxt = new HeadlineTextField(h1Format); headlineTxt.wordWrap = true; headlineTxt.x = this.paddingLeft; headlineTxt.y = this.paddingTop; headlineTxt.width = this.preferredWidth; this.addChild(headlineTxt); headlineTxt.fitText(this.headline, 1, true); subtitleTxt = new HeadlineTextField(h2Format); subtitleTxt.wordWrap = true; subtitleTxt.x = this.paddingLeft; subtitleTxt.y = headlineTxt.y + headlineTxt.height; subtitleTxt.width = this.preferredWidth; this.addChild(subtitleTxt); subtitleTxt.fitText(this.subtitle, 2, false); storyTxt = new MultiColumnText(this.numColumns, 20, this.preferredWidth, 400, true, this.pFormat); storyTxt.x = this.paddingLeft; storyTxt.y = subtitleTxt.y + subtitleTxt.height + 10; this.addChild(storyTxt); storyTxt.text = this.content; ... 将一个字段的 y 属性设置为等于上一个字段的 y 属性加上其自身高度,这样即可将每个字段放置在上一个字段的下面。由于 HeadlineTextField 对象和 MultiColumnTextField 对象可以更改其高度以适应内容,因此需要进行这种动态位置计算。 更改字体大小以适合字段大小在给定要显示内容的宽度(以像素为单位)和最多行数的情况下,HeadlineTextField 会更改字体大小以使文本适合字段大小。如果文本短,字体大小很大,就会生成 tabloid 样式的标题。如果文本很长,那么字体大小较小。 下面所示的 HeadlineTextField.fitText() 方法的作用就是调整字体大小: public function fitText(msg:String, maxLines:uint = 1, toUpper:Boolean = false, targetWidth:Number = -1):uint { this.text = toUpper ? msg.toUpperCase() : msg; if (targetWidth == -1) { targetWidth = this.width; } var pixelsPerChar:Number = targetWidth / msg.length; var pointSize:Number = Math.min(MAX_POINT_SIZE, Math.round(pixelsPerChar * 1.8 * maxLines)); if (pointSize < 6) { // the point size is too small return pointSize; } this.changeSize(pointSize); if (this.numLines > maxLines) { return shrinkText(--pointSize, maxLines); } else { return growText(pointSize, maxLines); } } public function growText(pointSize:Number, maxLines:uint = 1):Number { if (pointSize >= MAX_POINT_SIZE) { return pointSize; } this.changeSize(pointSize + 1); if (this.numLines > maxLines) { // set it back to the last size this.changeSize(pointSize); return pointSize; } else { return growText(pointSize + 1, maxLines); } } public function shrinkText(pointSize:Number, maxLines:uint=1):Number { if (pointSize <= MIN_POINT_SIZE) { return pointSize; } this.changeSize(pointSize); if (this.numLines > maxLines) { return shrinkText(pointSize - 1, maxLines); } else { return pointSize; } } HeadlineTextField.fitText() 方法使用简单的递归技术来调整字体大小。首先,该方法推测文本中每个字符的平均像素数,并据此计算起始点大小。然后,它更改字体大小并检查文本中的文字是否换行,从而产生比最大值更多的文本行。如果文本行过多,则它会调用 shrinkText() 方法减小字体大小并重试。如果文本行不太多,则它会调用 growText() 方法增大字体大小并重试。当字体大小再增加一点即会产生过多行时,该过程即会停止。 在多列之间拆分文本MultiColumnTextField 类会在多个 TextField 对象中分布文本,然后将这些对象排列成报纸专栏的样式。 MultiColumnTextField() 构造函数首先创建一个由 TextField 对象构成的数组,每列一个对象,如下所示: for (var i:int = 0; i < cols; i++) { var field:TextField = new TextField(); field.multiline = true; field.autoSize = TextFieldAutoSize.NONE; field.wordWrap = true; field.width = this.colWidth; field.setTextFormat(this.format); this.fieldArray.push(field); this.addChild(field); } 每个 TextField 对象均使用 addChild() 方法添加到该数组中,并添加到显示列表中。 只要 StoryLayout 的 text 属性或 styleSheet 属性发生更改,该对象就会调用 layoutColumns() 方法来重新显示文本。layoutColumns() 方法会调用 getOptimalHeight() 方法,以计算在给定布局宽度内适合所有文本所需的正确像素高度。 public function getOptimalHeight(str:String):int { if (field.text == "" || field.text == null) { return this.preferredHeight; } else { this.linesPerCol = Math.ceil(field.numLines / this.numColumns); var metrics:TextLineMetrics = field.getLineMetrics(0); this.lineHeight = metrics.height; var prefHeight:int = linesPerCol * this.lineHeight; return prefHeight + 4; } } 首先,getOptimalHeight() 方法计算每列的宽度。然后设置数组中第一个 TextField 对象的宽度和 htmlText 属性。getOptimalHeight() 方法使用第一个 TextField 对象计算文本中自动换行文本的总行数,并据此确定每列中应有多少行。接下来,它调用 TextField.getLineMetrics() 方法以检索 TextLineMetrics 对象,该对象包含有关第一行的文本大小的详细信息。TextLineMetrics.height 属性用像素表示文本行的完整高度,包括上缘、下缘和前导。MultiColumnTextField 对象的最佳高度即为行高度乘以每列行数再加上 4(TextField 对象顶部边框和底部边框各 2 个像素)。 以下是完整 layoutColumns() 方法的代码: public function layoutColumns():void { if (this._text == "" || this._text == null) { return; } var field:TextField = fieldArray[0] as TextField; field.text = this._text; field.setTextFormat(this.format); this.preferredHeight = this.getOptimalHeight(field); var remainder:String = this._text; var fieldText:String = ""; var lastLineEndedPara:Boolean = true; var indent:Number = this.format.indent as Number; for (var i:int = 0; i < fieldArray.length; i++) { field = this.fieldArray[i] as TextField; field.height = this.preferredHeight; field.text = remainder; field.setTextFormat(this.format); var lineLen:int; if (indent > 0 && !lastLineEndedPara && field.numLines > 0) { lineLen = field.getLineLength(0); if (lineLen > 0) { field.setTextFormat(this.firstLineFormat, 0, lineLen); } } field.x = i * (colWidth + gutter); field.y = 0; remainder = ""; fieldText = ""; var linesRemaining:int = field.numLines; var linesVisible:int = Math.min(this.linesPerCol, linesRemaining); for (var j:int = 0; j < linesRemaining; j++) { if (j < linesVisible) { fieldText += field.getLineText(j); } else { remainder +=field.getLineText(j); } } field.text = fieldText; field.setTextFormat(this.format); if (indent > 0 && !lastLineEndedPara) { lineLen = field.getLineLength(0); if (lineLen > 0) { field.setTextFormat(this.firstLineFormat, 0, lineLen); } } var lastLine:String = field.getLineText(field.numLines - 1); var lastCharCode:Number = lastLine.charCodeAt(lastLine.length - 1); if (lastCharCode == 10 || lastCharCode == 13) { lastLineEndedPara = true; } else { lastLineEndedPara = false; } if ((this.format.align == TextFormatAlign.JUSTIFY) && (i < fieldArray.length - 1)) { if (!lastLineEndedPara) { justifyLastLine(field, lastLine); } } } } 通过调用 getOptimalHeight() 方法设置 preferredHeight 属性后,layoutColumns() 方法会循环访问各个 TextField 对象,将每个对象的高度设置为 preferredHeight 值。然后,layoutColumns() 方法会为每个字段分布刚好足够的文本行,以使每个字段中都不会发生滚动,并且每个连续字段中的文本均会从前一个字段的文本结束处开始。如果文本对齐样式已设置为“justify”,则会调用 justifyLastLine() 方法以对齐字段中最后一行文本。否则,最后一行将被视为段落结束行,而不予对齐。 |
|