В примере News Layout текст форматируется в виде газетной статьи. Вводимый текст может содержать заголовок, подзаголовок и тело статьи. Используя заданные ширину и высоту отображения, пример News Layout форматирует заголовок и подзаголовок так, чтобы они занимали всю ширину области отображения. Текст статьи распределяется по двум или более столбцам.
В этом примере демонстрируется использование следующих приемов программирования на ActionScript:
-
расширение класса TextField;
-
загрузка и применение внешнего CSS-файла;
-
преобразование стилей CSS в объекты TextFormat;
-
использование класса TextLineMetrics для получения информации о размере области отображения текста.
Получить файлы приложения для этого примера можно на странице
www.adobe.com/go/learn_programmingAS3samples_flash_ru
. Файлы приложения News Layout находятся в папке Samples/NewsLayout. Приложение состоит из следующих файлов.
File
|
Описание
|
NewsLayout.mxml
или
NewsLayout.fla
|
Пользовательский интерфейс приложения для Flex (MXML) или Flash (FLA).
|
com/example/programmingas3/newslayout/StoryLayoutComponent.as
|
Класс UIComponent Flex, который размещает экземпляр StoryLayout.
|
com/example/programmingas3/newslayout/StoryLayout.as
|
Основной класс ActionScript, упорядочивающий все компоненты статьи новостей, предназначенной для отображения.
|
com/example/programmingas3/newslayout/FormattedTextField.as
|
Подкласс класса TextField, управляющий собственным объектом TextFormat.
|
com/example/programmingas3/newslayout/HeadlineTextField.as
|
Подкласс класса FormattedTextField, регулирующий размеры шрифта в соответствии с требуемой шириной.
|
com/example/programmingas3/newslayout/MultiColumnTextField.as
|
Класс ActionScript, разбивающий текст на два столбца или более.
|
story.css
|
CSS-файл, определяющий стили текста для макета.
|
Чтение внешнего CSS-файла
Приложение News Layout сначала выполняет чтение текста статьи из локального 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 изменяет размер шрифта так, чтобы текст поместился в поле. Если текст короткий, используется большой размер шрифта, в результате чего заголовок приобретает стиль таблоида. Если текст длинный, то размер шрифта становится меньше.
Метод
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()
.
Каждый раз, когда изменяется свойство
text
или
styleSheet
StoryLayout, этот класс вызывает метод
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()
вычисляет ширину каждого столбца. Затем он устанавливает ширину и свойство
htmlText
для первого объекта TextField в массиве. Метод
getOptimalHeight()
использует первый объект TextField для получения общего числа строк с переносом слов в тексте, в соответствии с которым он затем определяет количество строк в каждом столбце. После этого вызывается метод
TextField.getLineMetrics()
для получения объекта TextLineMetrics, содержащего сведения о размере текста в первой строке. Свойство
TextLineMetrics.height
представляет полную высоту строки текста в пикселах, включая надстрочные и подстрочные выносные элементы и междустрочный пробел. Таким образом, оптимальная высота объекта MultiColumnTextField равняется высоте строки, умноженной на количество строк в столбце, плюс 4 пиксела для рамки вверху и внизу объекта TextField, равной двум пикселам.
Ниже приводится код полного метода
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);
}
}
}
}
После определения свойства
preferredHeight
с помощью метода
getOptimalHeight()
метод
layoutColumns()
выполняет повторное прохождение через все объекты TextField, устанавливая значение
preferredHeight
в качестве высоты для каждого из них. Затем метод
layoutColumns()
распределяет каждому текстовому полю достаточное количество строк, чтобы в них не требовалась прокрутка, и текст в каждом последующем столбце начинался с того места, на котором закончился предыдущий. Если стилю выравнивания текста задано значение justify, то вызывается метод
justifyLastLine()
для выравнивания последней строки текста в поле. В противном случае последняя строка обрабатывается как последняя строка абзаца и не выравнивается.
|
|
|