I made a Spark component that derives from Label and truncates text in the middle. It's just a re-make based strongly on it's MX version:
http://cookbooks.adobe.com/post_Graphical_truncating_of_texts_and_labels__custom_t-13306.html
Here's the code for Spark version:
package
{import flash.text.TextField;
import mx.core.UITextField;
import mx.core.UITextFormat;
import mx.core.mx_internal;
import mx.managers.ISystemManager;
import spark.components.Label;
use namespace mx_internal;
public class MiddleTruncatingLabel extends Label
{
public function MiddleTruncatingLabel()
{
}
private var _trueText:String;
private var _textChanged:Boolean = false;
private var _isTruncated:Boolean = false;
override public function set text(value:String):void
{
// Check if value changed and indicate that
// to initiate new middle-truncating
// Remember original text
if (value != _trueText)
{
_trueText = value;
super.text = truncateTextMiddle(_trueText, unscaledWidth);
_textChanged = true;
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// check if need truncate and truncate
if ( !isNaN(unscaledWidth) && _textChanged )
{
// override textfield's text
text = truncateTextMiddle(_trueText, unscaledWidth);
// update info to check if need truncate to do not run
// middle-truncation too much of times
_textChanged = false;
}
}
public function truncateTextMiddle(fullText:String, widthToTruncate:Number) : String
{
if (!(fullText) || fullText.length < 3 || !this.parent)
{
// skip any truncating if no styles (no parent),
// or text is too small
return fullText;
}
// add paddings for some font oversize issues
var paddingWidth:Number =
UITextField.mx_internal::TEXT_WIDTH_PADDING +
this.getStyle("paddingLeft") + this.getStyle("paddingRight");
// Skip if width is too small
if (widthToTruncate < paddingWidth + 10) return fullText;
// Prepare measurement object
// We create new TextField, and copy styles for it from this object
// We cannot re-use internal original text field instance because
// it will cause event firing in process of text measurement
var measurementField:TextField = new TextField();
// Clear so measured text will not get old styles.
measurementField.text = "";
// Copy styles into TextField
var textStyles:UITextFormat = this.determineTextFormatFromStyles();
measurementField.defaultTextFormat = textStyles;
var sm:ISystemManager = this.systemManager;
if (textStyles.font)
{
measurementField.embedFonts = sm != null && sm.isFontFaceEmbedded(textStyles);
}
else
{
measurementField.embedFonts = false;
}
if (textStyles.antiAliasType) {
measurementField.antiAliasType = textStyles.antiAliasType;
}
if (textStyles.gridFitType) {
measurementField.gridFitType = textStyles.gridFitType;
}
if (!isNaN(textStyles.sharpness)) {
measurementField.sharpness = textStyles.sharpness;
}
if (!isNaN(textStyles.thickness)) {
measurementField.thickness = textStyles.thickness;
}
// Perform initial measure of text and check if need truncating at all
// To measure text, we set it to measurement text field
// and get line metrics for first line
measurementField.text = fullText;
var fullTextWidth:Number = measurementField.getLineMetrics(0).width + paddingWidth;
if(fullTextWidth > widthToTruncate){
// get width of ...
measurementField.text = "...";
var dotsWidth:Number = measurementField.getLineMetrics(0).width;
// Find out what is the half of truncated text without ...
var halfWidth : Number = (widthToTruncate - paddingWidth - dotsWidth) / 2;
// Make a rough estimate of how much chars we need to cut out
// This saves steps of character-by-character preocessing
measurementField.text = "s";
var charWidth:Number = measurementField.getLineMetrics(0).width;
var charsToTruncate:int = Math.round(
((fullTextWidth - paddingWidth) / 2 - halfWidth) /
charWidth) + 2;
// allow some distortion to account fractional widths part
halfWidth = halfWidth - 0.5;
// Below algorithm makes rough middle-truncating
// Then it is corrected by adding or removing
// characters for each part until reach required
// width for each half. Algorith does checks
// (min max and loop ciodnitions) so that string
// cannot be less then one character for each half
// see if right part of text approximately fits into half width
var rightPart:String;
var widthWithNextChar:Number;
var len:int = fullText.length;
var currLoc:int = Math.min(len/2 + charsToTruncate + 1, len-1);
measurementField.text = fullText.substr(currLoc);
var rightPartWidth:Number = measurementField.getLineMetrics(0).width;
if (rightPartWidth > halfWidth) {
// throw away characters until fits
currLoc++;
while (rightPartWidth > halfWidth && currLoc < len) {
measurementField.text = fullText.charAt(currLoc);
rightPartWidth -= measurementField.getLineMetrics(0).width;
currLoc++;
}
rightPart = fullText.substr(currLoc - 1);
} else {
// try to add characters one-by-one and
// see if it still fits
widthWithNextChar = 0;
do {
currLoc--;
rightPartWidth += widthWithNextChar;
measurementField.text = fullText.charAt(currLoc);
widthWithNextChar = measurementField.getLineMetrics(0).width;
} while (rightPartWidth + widthWithNextChar <= halfWidth && currLoc > 0);
rightPart = fullText.substr(currLoc + 1);
}
// Do the same with left part, but compare overall string
// Overall is needed because character-by character
// would not give us correct total width of string -
// somehow overall text is measured with sapcers etc. and
// also there are rounding issues.
// This way, and by putting left part calculating as last, we allow
// left part might be larger (may become more than half).
// allow some distortion in widths fractions
widthToTruncate = widthToTruncate - 0.5 - paddingWidth;
currLoc = Math.max(len/2 - charsToTruncate, 1);
measurementField.text = fullText.substr(0, currLoc) +
"..." + rightPart;
var truncatedWidth:Number = measurementField.getLineMetrics(0).width;
if (truncatedWidth > widthToTruncate) {
// throw away characters until fits
currLoc--;
while (truncatedWidth > widthToTruncate && currLoc > 0) {
measurementField.text = fullText.substr(0, currLoc) +
"..." + rightPart;
truncatedWidth = measurementField.getLineMetrics(0).width;
currLoc--;
}
currLoc++;
} else {
// try to add characters one-by-one and
// see if it still fits
do {
currLoc++;
measurementField.text = fullText.substr(0, currLoc) +
"..." + rightPart;
widthWithNextChar = measurementField.getLineMetrics(0).width;
} while (widthWithNextChar <= widthToTruncate &&
currLoc < len-1);
currLoc--;
}
return fullText.substr(0, Math.max(currLoc,1)) +
"..." + rightPart;
}
return fullText;
}
}
}