Przykład News Layout formatuje tekst do postaci przypominającej artykuł w drukowanej gazecie. Tekst wejściowy może zawierać nagłówek, podtytuł i treść artykułu. Biorąc pod uwagę szerokość i wysokość ekranu, przykładowa aplikacja News Layout formatuje nagłówek i podtytuł tak, aby zajmowały całą szerokość obszaru wyświetlania. Treść artykułu jest dzielona na dwie lub większą liczbę kolumn.
Ten przykład ilustruje następujące techniki programowania w języku ActionScript:
-
Rozszerzanie klasy TextField.
-
Ładowanie i stosowanie zewnętrznego pliku CSS.
-
Przekształcanie stylów CSS w obiekty TextFormat.
-
Wykorzystanie klasy TextLineMetrics do pobrania informacji o wielkości obszaru wyświetlania.
Aby pobrać pliki tej przykładowej aplikacji, należy przejść na stronę
www.adobe.com/go/learn_programmingAS3samples_flash_pl
. Pliki aplikacji News Layout znajdują się w folderze Samples/NewsLayout. Aplikacja składa się z następujących plików:
File
|
Opis
|
NewsLayout.mxml
lub
NewsLayout.fla
|
Interfejs użytkownika dla aplikacji Flex (MXML) lub Flash (FLA).
|
com/example/programmingas3/newslayout/StoryLayoutComponent.as
|
Klasa Flex UIComponent, która umieszcza instancję klasy StoryLayout.
|
com/example/programmingas3/newslayout/StoryLayout.as
|
Główna klasa języka ActionScript, która rozmieszcza wszystkie składniki artykułu przeznaczone do wyświetlenia.
|
com/example/programmingas3/newslayout/FormattedTextField.as
|
Podklasa klasy TextField, która zarządza własnym obiektem TextFormat.
|
com/example/programmingas3/newslayout/HeadlineTextField.as
|
Podklasa klasy FormattedTextField, która dopasowuje wielkości czcionek tak, aby tekst zmieścił się na żądanej szerokości.
|
com/example/programmingas3/newslayout/MultiColumnTextField.as
|
Klasa języka ActionScript, która dzieli tekst między dwie lub większą liczbę kolumn.
|
story.css
|
Plik CSS, który definiuje style tekstu dla układu.
|
Odczytywanie zewnętrznego pliku CSS
Działanie aplikacji News Layout rozpoczyna się od odczytania tekstu artykułu z lokalnego pliku XML. Następnie odczytywany jest zewnętrzny plik CSS zawierający informacje o formatowaniu nagłówka, podtytułu i tekstu głównego.
W pliku CSS zdefiniowane są trzy style: standardowy styl akapitu artykułu oraz style h1 i h2, odpowiednio dla nagłówka i podtytułu.
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;
}
Technika używana do odczytu zewnętrznego pliku CSS jest taka sama, jak technika opisana w sekcji
Ładowanie zewnętrznego pliku CSS
. Po załadowaniu pliku CSS aplikacja wykonuje metodę
onCSSFileLoaded()
przedstawioną poniżej.
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();
}
Metoda
onCSSFileLoaded()
tworzy obiekt StyleSheet i powoduje, że obiekt ten analizuje wejściowe dane CSS. Główny tekst artykułu jest wyświetlany w obiekcie MultiColumnTextField, który może bezpośrednio korzystać z obiektu StyleSheet. Jednak pola nagłówków są obiektami klasy HeadlineTextField, która do formatowania używa obiektu TextFormat.
Metoda
onCSSFileLoaded()
dwukrotnie wywołuje metodę
getTextStyle()
w celu przekształcenia deklaracji stylów CSS w obiekt TextFormat przeznaczony do użycia z oboma obiektami 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;
}
W deklaracjach stylów CSS i obiektach TextFormat stosowane są różne nazwy właściwości i znaczenia wartości właściwości. Metoda
getTextStyle()
tłumaczy wartości właściwości arkusza stylów CSS na wartości oczekiwane przez obiekt TextFormat.
Układanie elementów artykułu na stronie
Klasa StoryLayout formatuje i rozmieszcza pola tekstowe nagłówka, podtytułu i głównego tekstu w układzie „gazetowym”. Metoda
displayText()
tworzy i wstępnie rozmieszcza różne pola.
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;
...
Każde pole jest umieszczane pod poprzednim polem poprzez ustawienie właściwości
y
na wartość właściwości
y
poprzedniego pola powiększoną o jego wysokość. Dynamiczne obliczanie położenia jest wymagane, ponieważ wysokość obiektów HeadlineTextField i MultiColumnTextField może ulegać zmianie w wyniku dopasowania do treści.
Zmiana wielkości czcionki w celu dopasowania do wielkości pola
Na podstawie znanej szerokości w pikselach i maksymalnej liczby wierszy do wyświetlania obiekt HeadlineTextField dostosowuje wielkość czcionki w taki sposób, aby tekst zmieścił się w polu. Jeśli tekst jest krótki, czcionka zostanie powiększona, przez co nagłówek stanie się „krzykliwy”, niczym w tabloidach. Jeśli tekst jest długi, czcionka będzie mniejsza.
Operację zmiany wielkości czcionki realizuje metoda
HeadlineTextField.fitText()
przedstawiona poniżej:
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;
}
}
Metoda
HeadlineTextField.fitText()
wykorzystuje prostą technikę rekurencyjną do określenia wielkości czcionki. Najpierw szacunkowo określa średnią liczbę pikseli przypadającą na znak tekstu i na tej podstawie oblicza wstępnie wielkość czcionki w punktach. Następnie zmienia wielkość czcionki i sprawdza, czy w wyniku zawijania wierszy powstała liczba wierszy większa od możliwej do wyświetlenia. Jeśli liczba wierszy jest za duża, wywoływana jest metoda
shrinkText()
w celu zmniejszenia czcionki i podejmowana jest kolejna próba. Jeśli liczba wierszy NIE jest za duża, wywoływana jest metoda
growText()
w celu zwiększenia czcionki i podejmowana jest kolejna próba. Proces kończy się, gdy zwiększenie czcionki o jeden punkt spowodowałoby powstanie zbyt dużej liczby wierszy.
Podział tekstu między wiele kolumn
Klasa MultiColumnTextField rozkłada tekst między wiele obiektów TextField, które są następnie rozmieszczane podobnie, jak kolumny w gazecie.
Konstruktor
MultiColumnTextField()
tworzy najpierw tablicę obiektów TextField, po jednym dla każdej kolumny, co przedstawiono poniżej:
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);
}
Każdy obiekt TextField jest dodawany do tablicy, a następnie dodawany do listy wyświetlania wewnątrz metody
addChild()
.
Każda zmiana właściwości
text
lub
styleSheet
obiektu StoryLayout powoduje wywołanie metody
layoutColumns()
w celu ponownego wyświetlenia tekstu. Metoda
layoutColumns()
wywołuje metodę
getOptimalHeight()
w celu ustalenia prawidłowej liczby pikseli, która pozwoli zmieścić cały tekst w układzie o danej szerokości.
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;
}
}
Najpierw metoda
getOptimalHeight()
oblicza szerokość każdej kolumny. Następnie ustawia szerokość i właściwość
htmlText
pierwszego obiektu TextField w tablicy. Metoda
getOptimalHeight()
wykorzystuje ten pierwszy obiekt TextField do ustalenia łącznej liczby wierszy powstałych w wyniku zawijania tekstu i na tej podstawie określa wymaganą liczbę wierszy w każdej kolumnie. Następnie wywołuje metodę
TextField.getLineMetrics()
w celu pobrania obiektu TextLineMetrics zawierającego szczegółowe informacje o wielkości tekstu w pierwszym wierszu. Właściwość
TextLineMetrics.height
określa pełną wysokość wiersza tekstu w pikselach, włączając w to elementy pod linią bazową, nad górną krawędzią i interlinię. Optymalną wysokością obiektu MultiColumnTextField jest wówczas wysokość wiersza pomnożona przez liczbę wierszy w każdej kolumnie plus 4 (w celu uwzględnienia górnej i dolnej krawędzi obiektu TextField, która ma grubość dwóch pikseli).
Oto cały kod metody
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);
}
}
}
}
Po nadaniu wartości właściwości
preferredHeight
poprzez wywołanie metody
getOptimalHeight()
metoda
layoutColumns()
iteracyjnie przechodzi przez obiekty TextField, nadając każdemu z nich wysokość równą
preferredHeight
. Teraz metoda
layoutColumns()
rozmieszcza tyle wierszy tekstu w każdym polu, by poszczególne pola nie wymagały przewijania, a tekst w każdym następnym polu zaczynał się tam, gdzie skończył się tekst w polu poprzednim. Jeśli wybranym stylem wyrównanie tekstu jest „justify”, wywoływana jest metoda
justifyLastLine()
w celu wyjustowania ostatniego wiersza tekstu w polu. W przeciwnym razie ostatnia wersja zostałaby potraktowana jako ostatni wiersz akapitu i nie byłaby justowana.
|
|
|