Wednesday, January 30, 2008

Flex - Using Built-In measureText(txt) to Calculate a Text Objects Inner Text Width and Height Dimensions

I have been doing some Flex research with co-worker Ryan Swanson recently for a project and one of the things I needed to get to work in a certain way, was being able to scale a container based on the Text objects inner content text or text value dimensions. It needed to be very specific in it's sizing and just setting width and height to 100% did not cut it. This means that we needed to have a parent container dynamically scale based on whatever text you enter and whatever font you use and not have to do tightly coupled font surgery everywhere (gross). It has to be fluid and it needed to be calculated in a way that wasn't uber nasty. The parsing and calculation of text can get pretty ugly sometimes and I really wanted to see if there was something that could be used that was built into the Flex Framework. Well, after a few hours of research and constant testing I finally decided to look and see how the Alert class worked. After reading some of Doug McCune's blog posts ( Read his post about component development ), I have been getting bolder and just digging into the core Flex classes and seeing how things are done and seeing if we can either use something they built, or extend of that base. It turns out that all I needed to do was Control + Click (Windows) the Alert Component and just see that they use the measureText function to calculate size. Now I just discovered this so I'm not some super measureText wizard or anything. I just discovered that it allows for the calculation of the Text object's text characters and their pixel Width and Height without any extra crazy logic to loop through the text object's text value on every single change event. This was what I wanted to avoid if possible. Maybe the UIComponent is doing this logic deep down, but I'll have to look further tomorrow. For now, I just know it works the way we needed it and it's built into the Flex code base. So why not use it? Maybe there is a better way. But I just wanted to share my discovery. It so far has saved some time and also helped me dig into the Flex code Base a little further which is a great way to see all the gears moving :)

Here is the method we used for the test embedded below:

measureText(text:String):TextLineMetrics

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*">
<mx:VBox width="100%" height="100%" horizontalAlign="left" paddingTop="10" paddingLeft="10" verticalGap="10">
<mx:Label text="Enter text and watch the container stretch based on the measureText(text) value." color="white" />
<mx:Text text="The important concept here is using measureText(text) to calculate container width or height. It's super basic but measureText(text) is helpful because it calculates the width of the text objects inner text dimensions. Through Binding this is easy as the text measurements have been binded to the container up to the max dimensions." width="430" color="white"/>
<mx:HBox height="100%">
<mx:VBox width="100%" height="100%" verticalGap="10" verticalAlign="top">
<mx:TextArea id="flTextArea" width="200" height="200"/>
<mx:Text width="100%" text="{'Character Length: ' + autoTest1.text.length}" color="white"/>
<mx:Text width="100%" text="{'Actual Text Content Width: ' + Math.min(measureText(autoTest1.text).width+5, numericStepper.value)}" color="white" />
<mx:HBox width="100%" height="30" verticalAlign="middle">
<mx:Label text="Set Max Width:" color="white"/>
<mx:Spacer width="100%" />
<mx:NumericStepper id="numericStepper" minimum="0" maximum="200" stepSize="10" value="200"/>
</mx:HBox>
</mx:VBox>
<mx:VBox id="textContainer" backgroundColor="gray" borderStyle="solid" borderColor="white" paddingLeft="0" paddingRight="0">
<mx:Text id="autoTest1" width="{Math.min(measureText(autoTest1.text).width+5, numericStepper.value)}" maxHeight="200" color="white" text="{flTextArea.text}" paddingLeft="0" paddingRight="0" />
</mx:VBox>
</mx:HBox>
</mx:VBox>
</mx:Application>

"Returns measurement information for the specified text, assuming that it is displayed in a single-line UITextField component, and using this UITextFormat object to define the text format."


http://livedocs.adobe.com/flex/201/langref/mx/core/UITextFormat.html#measureText()






Download this source file here >>


3 comments:

Zenkey Group said...

Hi
This is great but your source link is broken? Could you re-post the zip file?

Thanks
Boyd

Anonymous said...

I had the same problem, but I also had to find out the height of the text. You cannot use the textLineMetrics, because it only calculates one line.

I end up using a (dummy) Text component and read the measuredWidth and measuredHeight of the Text component. I also had to add the Text to the canvas and call validateNow, to make it render at correct size.

Example:

var tempText:Text = new Text();
tempText.htmlText = "test";

this.addChild(tempText);
tempText.validateNow();

var width = tempText.measuredWidth;
var height = tempText.measuredHeight;
this.removeChild(tempText);

Dan said...

Thanks for posting this. It appears as if the width is only being measured on the top line. If a line below the top line is longer, then "Actual Text Content Width" remains the same width as the top line.