Ejemplo de TextField: Formato de texto con estilo periodístico
Flash Player 9 y posterior, Adobe AIR 1.0 y posterior
El ejemplo News Layout aplica formato a texto para que parezca una noticia de un periódico impreso. El texto de entrada puede contener un titular, un subtítulo y el cuerpo de la noticia. Dadas la anchura y la altura de visualización, el ejemplo News Layout aplica formato al titular y al subtítulo para que ocupen toda la anchura del área de visualización. El texto de la noticia se distribuye en dos o más columnas.
En este ejemplo se ilustran las siguientes técnicas de programación en ActionScript:
-
Ampliación de la clase TextField
-
Cargar y aplicar un archivo CSS externo
-
Convertir estilos de CSS en objetos TextFormat
-
Utilizar la clase TextLineMetrics para obtener información sobre el tamaño de la visualización de texto
Para obtener los archivos de la aplicación de este ejemplo, consulte
www.adobe.com/go/learn_programmingAS3samples_flash_es
. Los archivos de la aplicación News Layout se encuentran en la carpeta Samples/NewsLayout. La aplicación consta de los siguientes archivos:
Archivo
|
Descripción
|
NewsLayout.mxml
o
NewsLayout.fla
|
La interfaz de usuario de la aplicación para Flex (MXML) o Flash (FLA).
|
com/example/programmingas3/newslayout/StoryLayoutComponent.as
|
Una clase UIComponent de Flex que coloca la instancia de StoryLayout.
|
com/example/programmingas3/newslayout/StoryLayout.as
|
La clase principal de ActionScript que dispone todos los componentes de una noticia para mostrarla.
|
com/example/programmingas3/newslayout/FormattedTextField.as
|
Una subclase de la clase TextField que administra su propio objeto TextFormat.
|
com/example/programmingas3/newslayout/HeadlineTextField.as
|
Una subclase de la clase FormattedTextField que ajusta el tamaño de las fuentes a una anchura deseada.
|
com/example/programmingas3/newslayout/MultiColumnTextField.as
|
Una clase de ActionScript que divide el texto en dos o más columnas.
|
story.css
|
Un archivo CSS que define estilos de texto para el diseño.
|
Lectura del archivo CSS externo
La aplicación News Layout empieza por leer en un archivo XML local el texto de la noticia. A continuación, lee el archivo CSS externo que proporciona la información de formato para el titular, el subtítulo y el texto principal.
El archivo CSS define tres estilos, un estilo de párrafo estándar para la noticia y los estilos h1 y h2 para el titular y el subtítulo respectivamente.
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;
}
La técnica usada para leer el archivo CSS externo es la misma que la descrita en
Carga de un archivo CSS externo
. Cuando el archivo CSS se ha cargado, la aplicación ejecuta el método
onCSSFileLoaded()
, tal y como se muestra a continuación.
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();
}
El método
onCSSFileLoaded()
crea un objeto StyleSheet y hace que analice los datos CSS de entrada. El texto principal de la historia se muestra en un objeto MultiColumnTextField que puede utilizar un objeto StyleSheet directamente. No obstante, los campos de titular usan la clase HeadlineTextField, que utiliza un objeto TextFormat para su formato.
El método
onCSSFileLoaded()
llama dos veces a
getTextStyle()
para convertir una declaración de estilos CSS en un objeto TextFormat para utilizarlo con cada uno de los dos objetos 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;
}
Los nombres de propiedad y el significado de los valores de propiedad difieren entre las declaraciones de estilos CSS y los objetos TextFormat. El método
getTextStyle()
traduce los valores de propiedades CSS en los valores que espera el objeto TextFormat.
Disposición de los elementos de la noticia en la página
La clase StoryLayout aplica formato a los campos de titular, subtítulo y texto principal y los dispone como en un periódico. El método
displayText()
crea y sitúa inicialmente los distintos campos.
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;
...
Cada uno de los campos se coloca bajo el anterior estableciendo su propiedad
y
en un valor igual al de la propiedad
y
del campo anterior más su altura. Este cálculo dinámico de la posición es necesario, ya que los objetos HeadlineTextField y MultiColumnTextField pueden modificar su altura para ajustarla a su contenido.
Modificación del tamaño de fuente para ajustar el tamaño de un campo
Dados una anchura en píxeles y un número máximo de líneas para mostrar, HeadlineTextField modifica el tamaño de fuente para ajustar el texto al campo. Si el texto es corto, el tamaño de fuente es muy grande, y se creará un titular de tipo tabloide. Si el texto es largo, tamaño de fuente es más pequeño.
El método
HeadlineTextField.fitText()
que se muestra a continuación ajusta el tamaño de fuente:
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;
}
}
El método
HeadlineTextField.fitText()
utiliza una técnica recursiva sencilla para ajustar el tamaño de la fuente. En primer lugar calcula un número de píxeles por carácter en el texto y con ese dato calcula un tamaño de punto inicial. A continuación cambia el tamaño de la fuente y comprueba si el texto se ha ajustado para crear un número de líneas de texto superior al máximo. Si hay demasiadas líneas, llama al método
shrinkText()
para reducir el tamaño de fuente y lo intenta de nuevo. Si no hay demasiadas líneas, llama al método
growText()
para aumentar el tamaño de fuente y lo intenta de nuevo. El proceso se detiene en el punto en el que, al aumentar un punto el tamaño de fuente, se crearán demasiadas líneas.
División del texto en varias columnas
La clase MultiColumnTextField divide el texto en varios objetos TextField que se disponen como las columnas de un periódico.
El constructor
MultiColumnTextField()
crea primero un conjunto de objetos TextField, uno por cada columna, tal y como se muestra a continuación:
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);
}
Todos los objetos TextField se añaden al conjunto y a la lista de visualización con el método
addChild()
.
Siempre que cambien las propiedades
text
o
styleSheet
del objeto StoryLayout, se llama al método
layoutColumns()
para volver a mostrar el texto. El método
layoutColumns()
llama al método
getOptimalHeight()
method, para calcular la altura en píxeles necesaria para ajustar todo el texto en la anchura de diseño especificada.
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;
}
}
En primer lugar, el método
getOptimalHeight()
calcula la anchura de cada columna. Posteriormente se establece la anchura y la propiedad
htmlText
del primer objeto TextField en el conjunto. El método
getOptimalHeight()
utiliza ese primer objeto TextField para descubrir el número total de líneas ajustadas en el texto y, a partir de ese número, determina cuántas líneas debe haber en cada columna. Posteriormente llama al método
TextField.getLineMetrics()
para recuperar un objeto TextLineMetrics que contiene detalles sobre el tamaño del texto en la primera línea. La propiedad
TextLineMetrics.height
representa la altura total de una línea de texto, en píxeles, incluidos los valores ascendente, descendente y de interlineado. La altura óptima para el objeto MultiColumnTextField es la altura de línea multiplicada por el número de líneas por columna, más 4 para tener en cuenta el borde de dos píxeles que hay por encima y por debajo de un objeto TextField.
A continuación se muestra el código del método
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);
}
}
}
}
Una vez establecida la propiedad
preferredHeight
al llamar al método
getOptimalHeight()
, el método
layoutColumns()
recorre los objetos TextField y establece la altura de cada uno de ellos en el valor de
preferredHeight
. Posteriormente el método
layoutColumns()
distribuye a cada campo las líneas de texto suficientes para que no se produzca desplazamiento en ningún campo y el texto de cada campo empiece donde acabó el texto del campo anterior. Si el estilo de alineación del texto está establecido en "justify" (justificado), se llama al método
justifyLastLine()
para justificar la última línea del texto de un campo. De lo contrario, esa última línea se tratará como una línea de final de párrafo y no se justificará.
|
|
|
|
|