在 JavaFX 中,如何计算文本所占像素的宽度

在 JavaFX 中,如何计算文本所占像素的宽度

  JavaFX 的恼人之处在于很多基本的操作都要自己亲力亲为。有些时候,我们希望 TextArea 能自动根据文本内容换行以及调整大小。换行是 TextArea 中已经有的功能,不过这也带来了新的问题。在 JavaFX 中,可以使用 Text 对象的方法 .getBoundsInLocal().getWidth() 测得文本的基本尺寸。不过,依然有很多要注意的问题。


【注意】

  不同的符号的尺寸是不同的,不仅是中文汉字与英文字母,就连运算符、数字、英文字母之间的尺寸也不尽相同。不要自行编写自适应各种 Unicode 符号的算法。应该使用 JavaFX 内置的 API,如 Text 类的方法 Text对象.getBoundsInLocal().getWidth()


注意事项

  主要的注意事项如下:

  • 计算后最终的行宽值不会大于 TextArea 的行宽值。另外,当文本内容超过 TextArea 的最大宽度时,会触发换行。因此,还需要计算 TextArea 自动换行后的行数。

  • 用户输入的原始内容中就有可能包含换行。对这种情况可以有不同的处理方案,但是需要考虑这个问题。如果不选择直接去除换行符,则需要先将用户输入分割成各个不含换行符的文本,然后分别统计这些文本的行数与最大行宽,最后加行数相加,并来取这些最大行宽中的最大值。

  • Windows 会将回车解释成 \n\r,但 TextArea 清除文本中所有的 \r。换句话说,当在 Windows 输入回车时,实际上输入的是 \n\r。但当向 TextArea 输入 \n\r 时,TextArea 会移除所有的 \r。从 TextArea 得到的字符串中不会包含任何 \r,TextArea 的换行符为 \n

  • 行数只能是整数。另外,如果使用整数除法,需要将结果加 1。因为不满 1 行也需要占用一行。

  • 考虑用户的特殊输入:

    • 输入为空串

    • 输入的内容全是换行符

    • 输入的内容存在连续的换行符

    • 输入的开头是换行符

算法

  实现的算法大致如下:

(假设:当用户输入内容包含换行符时,不管是否连续,也直接进行原始输出而不删除。当用户输入的内容为空时,不报错,也直接视文本长度为 0 来计算)

  1. 判断输入是否为 null 或空串,如果是,视文本行宽为 0,行数为 1。如果不是,进行下一步。

  2. 判断输入是否只有换行符,如果是,视文本行宽为 0,行数为换行符的个数。如果不是,进行下一步。

  3. 将输入按照换行符分割成各个不含换行符的文本,然后先计算单个文本尺寸,最后叠加。方法如下:

    1. 计算单个文本的尺寸。方法如下:

      1. 求文本的原始像素长度。

      2. 计算文本的行宽值:取像素长度与单行文本最大长度之间的最小值。

      3. 计算文本的行数:将像素长度除以单行文本最大长度,然后向上取整。

    2. 计算整个文本的行宽值:取各个文本行宽值的最大值。

    3. 计算整个文本的行数:将各个文本行数值累加。

  4. 计算文本框的宽度:将文本的行宽值与文本框左右内边距相加。

  5. 计算文本框的长度:将文本的行数乘以单行文本行高,然后加上文本框上下内边距。

代码

  核心代码如下。

计算单行文本的像素宽度

public static double calculateTextPixelWidth(String text, Font font) {
    Text theText = new Text(text);
    theText.setFont(font);

    return theText.getBoundsInLocal().getWidth();
}

计算文本框尺寸

    /**
     * @param originText 内文本的内容
     * @param font 内文本的字体
     * @param lineSeparator 换行符的定义
     * @param originMaxWidth 内文本最大的行宽
     * @param rowExtension 对话框横向两端与内文本的边距
     * @param originSingleHeight 内文本一行的高度
     * @param columnExtension 对话框纵向向两端与内文本的边距
     * @return 计算出的对话框的宽度。其中,[0] 代表宽度,[1] 代表高度
     */
    public static double[] calculateTextBoxSize(String originText, Font font, String lineSeparator,
                                               double originMaxWidth, double rowExtension,
                                               double originSingleHeight, double columnExtension) {
        double maxRowLength = 0;
        int formattedColumnNum = 0;

        if (originText != null && !"".equals(originText)) {
            var texts = originText.split(lineSeparator);

            if (texts.length == 0) { // 如果文本中只有换行符
                maxRowLength = 0;
                formattedColumnNum = originText.length() + 1; // 注意要加 1
            } else {
                double singleRowLength = 0;
                for (var text : texts) {
                    var singleOriginWidth = calculateTextPixelWidth(text, font);
                    singleRowLength = Math.min(singleOriginWidth, originMaxWidth); // 注意:这是求最小值
                    maxRowLength = Math.max(maxRowLength, singleRowLength); // 注意:这里求最大值
                    formattedColumnNum += (int) (singleOriginWidth / originMaxWidth) + 1; // 注意要加 1
                }
            }
        }
        
        double[] result = new double[2];
        result[0] = maxRowLength + rowExtension * 2;
        result[1] = formattedColumnNum * originSingleHeight + columnExtension * 2;

        return result;
    }

  对于 TextArea,其换行符为 \n,因此可以使用如下代码:

/**
 * @param originText 内文本的内容
 * @param font 内文本的字体
 * @param originMaxWidth 内文本最大的行宽
 * @param rowExtension 对话框横向两端与内文本的边距
 * @param originSingleHeight 内文本一行的高度
 * @param columnExtension 对话框纵向向两端与内文本的边距
 * @return 计算出的对话框的宽度。其中,[0] 代表宽度,[1] 代表高度
 */
public static double[] calculateTextBoxSize(String originText, Font font,
                                            double originMaxWidth, double rowExtension,
                                            double originSingleHeight, double columnExtension) {
    String lineSeparator = "\n"; // TextArea 中的换行符为 '\n'

    return calculateTextBoxSize(originText, font, lineSeparator,
            originMaxWidth, rowExtension, originSingleHeight, columnExtension);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值