像素长度的字符串

8
我正在使用字符串数组集合填充下拉列表。我希望下拉列表控件的宽度与数组集合中最长字符串的大小(以像素为单位)相匹配。但我面临的问题是:集合中字符串的字体宽度不同,例如,“W”看起来比“l”更宽。因此,我估计字符宽度为8个像素,但这并不精确。如果遇到包含许多“W”和“M”的字符串,则估计就会出错。因此,我需要准确的像素字符串宽度。如何获得字符串的精确像素长度?
我的解决方案是将所有字符估计为8个像素宽:
public function populateDropDownList():void{
    var array:Array = new Array("o","two","three four five six seven eight wwww");
    var sampleArrayCollection:ArrayCollection = new ArrayCollection(array);
    var customDropDownList:DropDownList = new DropDownList();
    customDropDownList.dataProvider=sampleArrayCollection;
    customDropDownList.prompt="Select ...";
    customDropDownList.labelField="Answer Options:";
    //calculating the max width
    var componentWidth=10; //default
    for each(var answerText in array){
     Alert.show("Txt size: "+ answerText.length + " pixels: " + answerText.length*9); 
     if(answerText.length * 8 > componentWidth){
      componentWidth=answerText.length * 8;
     }
    }
    customDropDownList.width=componentWidth;
    answers.addChild(customDropDownList);
   }

任何想法或解决方案都非常重要。
谢谢。
4个回答

7
为了得到更准确的测量值,您可以使用一个TextField来填充字符串,然后测量该TextField文本的宽度。
代码:
function measureString(str:String, format:TextFormat):Rectangle {
    var textField:TextField = new TextField();
    textField.defaultTextFormat = format;
    textField.text = str;

    return new Rectangle(0, 0, textField.textWidth, textField.textHeight);
}

使用方法:

var format:TextFormat = new TextFormat();
format.font = "Times New Roman";
format.size = 16;

var strings:Array = [ "a", "giraffe", "foo", "!" ];

var calculatedWidth:Number = 50;    // Set this to minimum width to start with

for each (var str:String in strings) {
    var stringWidth:Number = measureString(str, format).width;
    if (stringWidth > calculatedWidth) {
        calculatedWidth = stringWidth;
    }
}

trace(calculatedWidth);

@Rose 相信你需要在分配字符串后但在检查 textWidth/textHeight 之前添加 validateNow() 调用。字符串的赋值会导致大小的失效,但大小的重新计算不是同步的,因此必须强制进行验证。 - shaunhusain
太好了!我喜欢这个解决方案,但是我有一个小问题。它没有得到正确的像素宽度。我在数组中给了它这个字符串。var strings:Array = [ "a", "giraffe", "foo", "The ArrayCollection object does not represent any" ];现在它得到的文本宽度是245,但实际大小是276。所以我的下拉列表上显示了滚动条。从你的代码中,我删除了具有textFormat的部分,因此我将measureStr函数变成了一个带有1个参数的函数。 - Rose
好的,我进行了一些测试:使用您提供的示例字符串,在字体大小为12、Times New Roman下,我得到了宽度为245。使用相同的字符串但使用字体大小为14的格式时,我得到了284的宽度。我不确定下拉列表使用的是哪种字体,但可能还有一些边距需要考虑。 - Cameron
@Rose:很高兴它能正常工作!我会删除“未经测试”的那部分;-) - Cameron
@subrat:嗯,我不确定为什么它不能处理非ASCII字符...如果你找到了解决方案,请发布为答案! - Cameron
显示剩余7条评论

1

我没有其他人帖子的编辑权限,所以我将其作为单独的答案发布,但如果这有效,应该归功于卡梅伦:

function measureString(str:String, format:TextFormat):Rectangle {
    var textField:TextField = new TextField();
    textField.autoSize = TextFieldAutoSize.LEFT;
    textField.defaultTextFormat = format;
    textField.text = str;

    return new Rectangle(0, 0, textField.textWidth, textField.textHeight);
}

如果我发现它确实被编辑了,为了保持整洁,我会删除这个。

一开始很抱歉发了垃圾帖子,本来想回答问题,但回答错误了...不管怎样,我测试了这个并且它似乎可以工作。我是用Flex做的,但你应该可以只使用AS3部分,没有问题,我只是将textfield包装在UIComponent中以便将其放在舞台上,但使用autosize似乎也可以正常工作:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <fx:Script>
        <![CDATA[
            import mx.core.UIComponent;
            protected function textinput1_changeHandler(event:TextOperationEvent):void
            {
                // TODO Auto-generated method stub

                var rect:Rectangle = measureString(event.target.text);
                holderBox.graphics.clear();
                holderBox.graphics.beginFill(0xFF0000);
                holderBox.graphics.drawRect(rect.x,rect.y,rect.width,rect.height);
            }

            private function measureString(str:String):Rectangle {
                var textField:TextField = new TextField();
                textField.autoSize = TextFieldAutoSize.LEFT;
                textField.text = str;
                var uiComponent:UIComponent = new UIComponent();
                uiComponent.addChild(textField);
                holderBox.addChild(uiComponent);

                return new Rectangle(0, 0, textField.textWidth, textField.textHeight);
            }

        ]]>
    </fx:Script>
    <mx:VBox>
        <s:TextInput text="" change="textinput1_changeHandler(event)"/>
        <mx:Box id="holderBox"/>
    </mx:VBox>
</s:Application>

抱歉,我有点冲动了,习惯在这些情况下使用validateNow...你很可能是对的,因为TextField是Flash组件而不是Flex组件,我所说的验证方案适用于Flex组件...虽然我也用过文本字段,但我认为你可能需要设置自动大小或类似的东西...几分钟后我会在上面发布编辑。 - shaunhusain
有趣...你确定是 autoSize 导致的问题,而不是文本框被添加到舞台上的原因? - Cameron
老实说,我不确定昨天下班时是否完成了这个任务,因为没有时间尝试纯AS3。今天我会再试一次纯AS3,我自己也很好奇。我只知道我需要autosize来使预加载文本字段正确调整大小。 - shaunhusain
谢谢Shaun和Cameron。我测试了你们的新代码。它只测量了动态添加的textField组件的大小,但是textInput中的文本大小不同(实际上比textField更大),你可以通过放大textInput组件来看到这一点。问题是,我认为这两个组件中的文本使用了不同的字体;但我不确定;对我来说是这样的。要看到这一点,请使用相同的代码:<s:TextInput text="" change="textinput1_changeHandler(event)" width="500"/> 谢谢。 - Rose
谢谢Shaun,问题在于两个组件之间的字体不匹配。我已经在第一篇帖子中放置了详细的评论。 - Rose

0

这是一些以上代码的更精细版本。考虑到换行符(html break和\n)并使用一些其他优化来使创建的TextField对象无效。希望这对您有所帮助。

function measureString(str:String, font:String="Times New Roman", size:Number=12):Rectangle 
{
    var textField:TextField = new TextField();
    textField.defaultTextFormat = new TextFormat( font, size );
    textField.border = true;
    textField.multiline = true;
    textField.autoSize = TextFieldAutoSize.LEFT;
    textField.htmlText = str;
    // Grab with and height before nullifying Textfield.
    var w:Number = textField.textWidth;
    var h:Number = textField.textHeight;
    //addChild( textField );// This will add the Textfield to the stage so you can visibly see it.
    //if( contains( textField ) ) removeChild( textField );// If it exists onstage, remove it.
    textField = null;//nullify it to make it available for garbage collection.
    return new Rectangle(0, 0, w, h);
}

var str:String = "Jeremy is a good boy.<br>He also has a red bike. \nSometimes Jeremy rides his bike to the store to buy bread for his family.<br>He likes wholewheat.";
trace( measureString( str, "Times New Roman", 25 ).width );

如果您更喜欢使用类的方式,请查看我的GIT框架: https://github.com/charlesclements/as3-tools/blob/master/net/charlesclements/util/text/TextUtil.as

AS3-tools:https://github.com/charlesclements/as3-tools

另外,我们的Flash/JS兄弟Jack Doyle @ GreenSock有一些方便处理文本的实用工具。非常值得去查看一下:http://www.greensock.com/splittext/

0
这是我为Java编写的例程。我猜你可以将其转换为JavaScript或其他语言。
虽然不是非常准确,但它给出了一个大概的答案。它假定一个空格大约宽200,M和W大约宽1000。显然,你需要根据你特定的字体大小进行缩放。
static int picaSize(String s)
{
    // the following characters are sorted by width in Arial font
    String lookup = " .:,;'^`!|jl/\\i-()JfIt[]?{}sr*a\"ce_gFzLxkP+0123456789<=>~qvy$SbduEphonTBCXY#VRKZN%GUAHD@OQ&wmMW";
    int result = 0;
    for (int i = 0; i < s.length(); ++i)
    {
        int c = lookup.indexOf(s.charAt(i));
        result += (c < 0 ? 60 : c) * 7 + 200;
    }
    return result;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接