TekstField-voorbeeld: tekstopmaak in krantenstijl
Flash Player 9 of hoger, Adobe AIR 1.0 of hoger
In het voorbeeld met nieuwslay-out wordt tekst opgemaakt als een artikel in een gedrukte krant. De invoertekst kan een kop, een subkop en de hoofdtekst van het artikel bevatten. Bij een bepaalde weergavebreedte en -hoogte, worden met dit nieuwslay-outvoorbeeld de kop en subkop zodanig opgemaakt dat de volledige breedte van het weergavegebied wordt gebruikt. De tekst van het artikel wordt verspreid over twee of meer kolommen.
In dit voorbeeld worden de volgende ActionScript-programmeringstechnieken geïllustreerd:
-
De klasse TextField uitbreiden
-
Een extern CSS-bestand laden en toepassen
-
CSS-stijlen converteren naar TextFormat-objecten
-
De klasse TextLineMetrics gebruiken om informatie over de tekstweergavegrootte te krijgen
Zie
www.adobe.com/go/learn_programmingAS3samples_flash_nl
als u de toepassingsbestanden voor dit voorbeeld wilt downloaden. De toepassingsbestanden van News Layout vindt u in de map Samples/NewsLayout. De toepassing bestaat uit de volgende bestanden:
Bestand
|
Beschrijving
|
NewsLayout.mxml
of
NewsLayout.fla
|
De gebruikersinterface voor de toepassing voor Flex (MXML) of Flash (FLA).
|
com/example/programmingas3/newslayout/StoryLayoutComponent.as
|
Een Flex-klasse UIComponent die de instantie StoryLayout plaatst.
|
com/example/programmingas3/newslayout/StoryLayout.as
|
De hoofdklasse van ActionScript waarmee alle componenten van een nieuwsartikel worden gerangschikt voor weergave.
|
com/example/programmingas3/newslayout/FormattedTextField.as
|
Een subklasse van de klasse TextField waarmee het eigen TextFormat-object wordt beheerd.
|
com/example/programmingas3/newslayout/HeadlineTextField.as
|
Een subklasse van de klasse FormattedTextField waarmee de lettertypegrootte wordt aangepast aan de gewenste breedte.
|
com/example/programmingas3/newslayout/MultiColumnTextField.as
|
Een ActionScript-klasse waarmee tekst over twee of meer kolommen wordt verdeeld.
|
story.css
|
Een CSS-bestand dat tekststijlen definieert voor de lay-out.
|
Het externe CSS-bestand lezen
De nieuwslay-outtoepassing start met het lezen van de artikeltekst uit een lokaal XML-bestand. Vervolgens wordt een extern CSS-bestand gelezen dat de opmaakinformatie bevat voor de kop, subkop en hoofdtekst.
In het CSS-bestand worden drie stijlen gedefinieerd: een standaardalineastijl voor het artikel en de stijlen h1 en h2 voor respectievelijk de kop en de subkop.
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;
}
De techniek die wordt gebruikt voor het lezen van het externe CSS-bestand is dezelfde als de techniek die wordt beschreven in
Een extern CSS-bestand laden
. Wanneer het CSS-bestand is geladen, voert de toepassing de hieronder getoonde methode
onCSSFileLoaded()
uit.
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();
}
Met de methode
onCSSFileLoaded()
wordt een StyleSheet-object gemaakt, dat de CSS-invoergegevens parseert. De hoofdtekst van het artikel wordt weergegeven in een MultiColumnTextField-object, dat rechtstreeks gebruik kan maken van een StyleSheet-object. Het kopveld maakt echter gebruik van de klasse HeadlineTextField, die een TextFormat-object gebruikt voor de opmaak.
Met de methode
onCSSFileLoaded()
wordt tweemaal de methode
getTextStyle()
aangeroepen om een CSS-stijldeclaratie te converteren naar een TextFormat-object dat kan worden gebruikt bij elk van de twee HeadlineTextField-objecten.
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;
}
De namen van de eigenschappen en de betekenis van de eigenschapwaarden verschillen tussen CSS-stijldeclaraties en TextFormat-objecten. Met de methode
getTextStyle()
worden CSS-eigenschapwaarden vertaald in de waarden die worden verwacht door het TextFormat-object.
Artikelelementen op de pagina rangschikken
Met de klasse StoryLayout worden de kop-, subkop- en hoofdtekstvelden opgemaakt en ingedeeld in een krantenopmaak. Met de methode
displayText()
worden in eerste instantie de verschillende velden gemaakt en geplaatst.
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;
...
Elk veld wordt onder het vorige veld geplaatst door de eigenschap
y
ervan gelijk te maken aan de eigenschap
y
van het vorige veld plus de hoogte van het veld. Deze dynamische plaatsingsberekening is nodig omdat HeadlineTextField-objecten en MultiColumnTextField-objecten hun hoogte kunnen aanpassen aan de inhoud.
Tekengrootte aanpassen aan de veldgrootte
Bij een bepaalde breedte in pixels en een maximumaantal regels dat kan worden weergegeven, past HeadlineTextField de tekengrootte aan zodat de tekst in het veld past. Als de tekst kort is, is de tekengrootte groot waardoor een koptekst in tabloidstijl ontstaat. Als de tekst lang is, wordt de tekengrootte kleiner.
De hieronder getoonde methode
HeadlineTextField.fitText()
past de tekengrootte aan:
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;
}
}
De methode
HeadlineTextField.fitText()
maakt gebruik van een eenvoudige recursieve techniek om de tekengrootte aan te passen. Eerst wordt een schatting gemaakt van het gemiddelde aantal pixels per teken in de tekst en op basis daarvan wordt een eerste puntgrootte berekend. Vervolgens wordt de tekengrootte gewijzigd en wordt gecontroleerd of er tekstomloop heeft plaatsgevonden om meer dan één regel tekst te maken. Als er te veel regels zijn, wordt de methode
shrinkText()
aangeroepen waarmee de tekengrootte wordt verkleind en opnieuw wordt gekeken. Als er niet te veel regels zijn, wordt de methode
growText()
aangeroepen waarmee de tekengrootte wordt vergroot en opnieuw wordt gekeken. Het proces stopt op het moment waarop het vergroten van de tekengrootte met één punt te veel regels zou opleveren.
Tekst splitsen over meerdere kolommen
Met de klasse MultiColumnTextField wordt tekst verspreid over meerdere TextField-objecten die vervolgens worden gerangschikt als krantenkolommen.
Met de constructor
MultiColumnTextField()
wordt eerst een array van TextField-objecten gemaakt, één voor elke kolom, zoals hier aangegeven:
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);
}
Elk TextField-object wordt met de methode
addChild()
toegevoegd aan de array en aan de weergavelijst.
Telkens wanneer de StoryLayout-eigenschap
text
of de eigenschap
styleSheet
wijzigt, wordt de methode
layoutColumns()
aangeroepen om de tekst opnieuw weer te geven. Met de methode
layoutColumns()
wordt de methode
getOptimalHeight()
aangeroepen om de juiste pixelhoogte te berekenen die nodig is om alle tekst binnen de beschikbare lay-outbreedte te passen.
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;
}
}
Met de methode
getOptimalHeight()
wordt eerst de breedte van elke kolom berekend. Vervolgens worden de breedte en de eigenschap
htmlText
van het eerste TextField-object in de array ingesteld. De methode
getOptimalHeight()
gebruikt dat eerste TextField-object om het totale aantal regels met regelomloop in de tekst te berekenen, en bepaalt op basis daarvan hoeveel regels elke kolom moet hebben. Vervolgens wordt de methode
TextField.getLineMetrics()
aangeroepen om een TextLineMetrics-object op te halen dat gegevens bevat over de grootte van de tekst op de eerste regel. De eigenschap
TextLineMetrics.height
geeft de volledige hoogte van een regel tekst weer (in pixels), inclusief stok, staart en regelafstand. De optimale hoogte voor het MultiColumnTextField-object is vervolgens de regelhoogte maal het aantal regels per kolom, plus 4 voor de rand van twee pixels boven en onder aan een TextField-object.
Hier is de code voor de volledige methode
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);
}
}
}
}
Wanneer de eigenschap
preferredHeight
is ingesteld door het aanroepen van de methode
getOptimalHeight()
, worden met de methode
layoutColumns()
de TextField-objecten doorlopen en wordt de hoogte van elk van de objecten ingesteld op de waarde
preferredHeight
. Met de methode
layoutColumns()
worden vervolgens net voldoende regels tekst in elk veld verdeeld zodat er geen verschuiving optreedt in de afzonderlijke velden en de tekst in elk volgende veld begint waar de tekst in het vorige veld eindigde. Als de tekstuitlijningsstijl is ingesteld op “uitvullen”, wordt de methode
justifyLastLine()
aangeroepen om de laatste regel tekst in een veld uit te vullen. Anders zou de laatste regel worden behandeld als een regel aan het einde van een alinea en niet worden uitgevuld.
|
|
|
|
|