TextField-Beispiel: Textformatierung im Zeitungsstil

Flash Player 9 und höher, Adobe AIR 1.0 und höher

In der Beispielanwendung „News Layout“ wird Text so formatiert, dass er einem Zeitungsartikel ähnelt. Der Eingabetext kann eine Schlagzeile, einen Untertitel und den Haupttext des Artikels enthalten. In Bezug auf die Breite und Höhe der Anzeige werden in der Beispielanwendung „News Layout“ die Schlagzeile und der Untertitel so formatiert, dass sie in voller Breite im Anzeigebereich dargestellt werden. Der Haupttext wird auf mindestens zwei Spalten aufgeteilt.

In diesem Beispiel werden die folgenden ActionScript-Programmiertechniken vermittelt:

  • Erweitern der TextField-Klasse

  • Laden und Anwenden einer externen CSS-Datei

  • Konvertieren von CSS-Stilen in TextFormat-Objekte

  • Abrufen von Daten zur Größe der Textanzeige mithilfe der TextLineMetrics-Klasse

Die Anwendungsdateien für dieses Beispiel finden Sie unter www.adobe.com/go/learn_programmingAS3samples_flash_de . Die Dateien der Anwendung „News Layout“ befinden sich im Ordner „Samples/NewsLayout“. Die Anwendung umfasst die folgenden Dateien:

Datei

Beschreibung

NewsLayout.mxml

oder

NewsLayout.fla

Die Benutzeroberfläche der Anwendung im Flex-Format (MXML) oder Flash-Format (FLA).

com/example/programmingas3/newslayout/StoryLayoutComponent.as

Eine UIComponent-Klasse von Flex, durch die die StoryLayout-Instanz eingefügt wird.

com/example/programmingas3/newslayout/StoryLayout.as

Die ActionScript-Hauptklasse, mit der alle Komponenten des Zeitungsartikels für die Anzeige angeordnet werden.

com/example/programmingas3/newslayout/FormattedTextField.as

Eine Unterklasse der TextField-Klasse, in der das zugehörige TextFormat-Objekt verwaltet wird.

com/example/programmingas3/newslayout/HeadlineTextField.as

Eine Unterklasse der FormattedTextField-Klasse, mit der die Schriftgröße so angepasst wird, dass der Text die gewünschte Breite hat.

com/example/programmingas3/newslayout/MultiColumnTextField.as

Eine ActionScript-Klasse, mit der Text auf mindestens zwei Spalten aufgeteilt wird.

story.css

Eine CSS-Datei, mit der Textformate für das Layout definiert werden.

Laden der externen CSS-Datei

Mit der Anwendung „News Layout“ wird zunächst der Text des Artikels aus einer lokalen XML-Datei geladen. Anschließend wird eine externe CSS-Datei mit Formatierungsdaten für die Schlagzeile, den Untertitel und den Haupttext geladen.

In der CSS-Datei sind drei Stile definiert, ein Standardabsatzformat für den Artikel sowie das h1-Format und das h2-Format für die Schlagzeile bzw. den Untertitel.

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; 
}

Das zum Lesen der externen CSS-Datei verwendete Verfahren entspricht dem Verfahren, das im Abschnitt Laden externer CSS-Dateien beschrieben wird. Nach dem Laden der CSS-Datei wird in der Anwendung die onCSSFileLoaded() -Methode ausgeführt, wie im Folgenden dargestellt.

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(); 
}

Mit der onCSSFileLoaded() -Methode wird ein StyleSheet-Objekt erstellt. Mit diesem Objekt werden dann die CSS-Eingabedaten analysiert. Der Haupttext des Artikels wird in einem MultiColumnTextField-Objekt angezeigt, in dem StyleSheet-Objekte direkt verwendet werden können. Für die Felder der Schlagzeile wird dagegen die HeadlineTextField-Klasse verwendet. Die entsprechende Formatierung erfolgt mithilfe eines TextFormat-Objekts.

Mit der onCSSFileLoaded() -Methode wird die getTextStyle() -Methode zweimal aufgerufen, um ein CSS-Stylesheet in ein TextFormat-Objekt zu konvertieren, das in jedem der beiden HeadlineTextField-Objekte verwendet wird.

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; 
}

Die Eigenschaftsnamen und die Bedeutung der Eigenschaftswerte unterscheiden sich bei CSS-Stylesheets und bei TextFormat-Objekten. Mit der getTextStyle() -Methode werden die CSS-Eigenschaftswerte in die im TextFormat-Objekt erwarteten Werte umgewandelt.

Anordnen der Artikelelemente auf der Seite

Mit der StoryLayout-Klasse werden die Formatierung und das Layout der Felder für Schlagzeile, Untertitel und Haupttext im Zeitungsstil durchgeführt. Mit der displayText() -Methode werden die verschiedenen Felder zunächst erstellt und dann positioniert.

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; 
...

Die einzelnen Felder werden jeweils unter dem vorherigen Feld platziert, indem für die y -Eigenschaft des jeweiligen Feldes der gleiche Wert wie für die y -Eigenschaft des vorherigen Feldes zuzüglich der Feldhöhe festgelegt wird. Diese dynamische Positionierungsberechnung ist erforderlich, da sich die Größe von HeadlineTextField-Objekten und MultiColumnTextField-Objekten entsprechend der Höhe des jeweiligen Inhalts ändern kann.

Ändern der Schriftgröße entsprechend der Feldgröße

Entsprechend der angegebenen Breite in Pixel und einer maximalen Anzahl anzuzeigender Zeilen wird die Schriftgröße im HeadlineTextField-Objekt so geändert, dass Text und Feld angepasst werden. Bei einem kurzen Text ist die Schrift groß, sodass eine Schlagzeile im Boulevardstil erstellt wird. Je länger der Text ist, umso kleiner ist die Schriftgröße.

Die Schriftgröße wird mit der HeadlineTextField.fitText() -Methode geändert, wie im Folgenden dargestellt:

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; 
    } 
}

In der HeadlineTextField.fitText() -Methode wird ein einfaches rekursives Verfahren zum Ändern der Schriftgröße verwendet. Zunächst wird die durchschnittliche Anzahl von Pixeln pro Zeichen im Text geschätzt und davon ausgehend eine anfängliche Punktgröße berechnet. Dann wird die Schriftgröße geändert und überprüft, ob im Text Zeilenumbrüche aufgetreten sind und eine die maximale Anzahl zulässiger Textzeilen übersteigende Anzahl von Zeilen entstanden ist. Wenn zu viele Zeilen vorhanden sind, wird die shrinkText() -Methode aufgerufen, um die Schriftgröße zu verringern. Anschließend wird der Vorgang wiederholt. Wenn die maximale Anzahl zulässiger Textzeilen nicht überschritten wurde, wird die growText() -Methode aufgerufen, um die Schriftgröße zu vergrößern. Dann wird der Vorgang wiederholt. Der Vorgang wird beendet, wenn durch Inkrementieren der Schriftgröße um einen weiteren Punkt zu viele Zeilen entstehen.

Aufteilen von Text auf mehrere Spalten

Mithilfe der MultiColumnTextField-Klasse wird Text auf mehrere TextField-Objekte aufgeteilt, die dann wie Zeitungsspalten angeordnet werden.

Mit dem MultiColumnTextField() -Konstruktor wird zunächst ein Array von TextField-Objekten erstellt, jeweils ein Objekt pro Zeile, wie im Folgenden dargestellt:

    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); 
    }

Jedes TextField-Objekt wird mithilfe der addChild() -Methode zum Array und zur Anzeigeliste hinzugefügt.

Wenn sich die text -Eigenschaft oder die styleSheet -Eigenschaft des StoryLayout-Objekts ändert, wird die layoutColumns() -Methode aufgerufen, damit der Text neu angezeigt wird. Mit der layoutColumns() -Methode wird die getOptimalHeight() -Methode aufgerufen, um die entsprechende Pixelhöhe zu ermitteln, die zum Anpassen des Textes an die angegebene Layoutbreite erforderlich ist.

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; 
    } 
}

Zunächst wird mithilfe der getOptimalHeight() -Methode die Breite jeder Spalte berechnet. Anschließend werden die Breite und die htmlText -Eigenschaft des ersten TextField-Objekts im Array festgelegt. Mithilfe der getOptimalHeight() -Methode wird anhand des ersten TextField-Objekts die Gesamtanzahl der Zeilen mit Zeilenumbruch im Text festgestellt und dann die Anzahl der in jeder Spalte erforderlichen Zeilen ermittelt. Dann wird die TextField.getLineMetrics() -Methode aufgerufen, um ein TextLineMetrics-Objekt mit detaillierten Daten zur Größe des Textes in der ersten Zeile abzurufen. Mit der TextLineMetrics.height -Eigenschaft wird die volle Höhe einer Textzeile in Pixel angegeben, einschließlich Oberlänge, Unterlänge und Durchschuss. Die optimale Höhe des MultiColumnTextField-Objekts ergibt sich dann aus der Zeilenhöhe multipliziert mit der Anzahl der Zeilen pro Spalte, plus 4 für die Ränder eines TextField-Objekts von jeweils zwei Pixel oben und unten.

Es folgt der vollständige Code für die layoutColumns() -Methode:

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); 
        } 
    } 
    } 
}

Nachdem die preferredHeight -Eigenschaft durch Aufrufen der getOptimalHeight() -Methode festgelegt wurde, werden die TextField-Objekte mithilfe der layoutColumns() -Methode durchlaufen und die Höhe jedes Textfelds jeweils auf den preferredHeight -Wert gesetzt. Anschließend wird mithilfe der layoutColumns() -Methode jedem Textfeld genau die Anzahl von Zeilen zugewiesen, bei der in keinem Textfeld ein Bildlauf durchgeführt werden muss und der Text in jedem Feld jeweils an der Stelle beginnt, an der der Text im vorherigen Feld endet. Wenn als Textausrichtungsstil „justify“ festgelegt wurde, wird anschließend die justifyLastLine() -Methode aufgerufen, um die abschließende Textzeile in einem Feld auszurichten. Andernfalls wird die letzte Zeile als Absatzende eingestuft und nicht ausgerichtet.