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() 方法以对齐字段中最后一行文本。否则,最后一行将被视为段落结束行,而不予对齐。 |
|