Java - 调整字体大小以适合区域
Java - Resizing font to fit area
多年来,我曾多次遇到调整文本大小以适应 Java GUI 上特定区域的问题。我的解决方案通常是通过以下方式解决问题:
重新设计界面以避免问题
更改区域以适应文本大小
对正确大小的字体进行二进制搜索以适合字符串(当我无法执行前两个操作时)
最近在做另一个需要快速确定给定区域的正确字体大小的项目时,我的二进制搜索方法太慢了(我怀疑是因为动态内存分配涉及多次创建和测量字体按顺序)并在我的应用程序中引入了明显的滞后。我需要的是一种更快更简单的方法来计算字体大小,允许呈现给定的字符串以适合 GUI 的定义区域。
最后我想到有一种更简单、更快捷的方法,只需要在 运行 时间进行几次分配。这种新方法消除了对任何类型搜索的需要,只需要进行一次测量,但是,它确实需要做出一个假设,该假设对于大多数应用来说都是完全合理的。
- 字体的宽度和高度必须与字体的磅值成正比。除了对渲染上下文所做的最模糊的转换外,这都会发生。
使用这个假设,我们可以计算字体尺寸与点大小的比率,并线性外推以找到给定区域所需的字体大小。我为此编写的一些代码如下:
编辑:
初始测量的准确性受基本字体大小的限制。使用非常小的字体作为基础可能会影响结果。但是base font越大越准确线性逼近越准确
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
public class FontUtilities
{
public static Font createFontToFit
(
String value,
double width,
double height,
Font base,
Graphics context
)
{
double measuredWidth;
double measuredHeight;
double baseFontSize;
FontMetrics ruler;
Rectangle2D bounds;
double heightBasedFontSize;
double widthBasedFontSize;
GlyphVector vector;
Shape outline;
if
(
(value == null) ||
(base == null) ||
(context == null) ||
(width != width) ||
(height != height)
)
{
return null;
}
//measure the size of the string in the current font size
baseFontSize = base.getSize2D();
ruler = context.getFontMetrics(base);
vector = base.createGlyphVector(ruler.getFontRenderContext(), value);
//use the bounds measurement on the outline of the text since this is the only
//measurement method that seems to be bug free and consistent in java
outline = vector.getOutline(0, 0);
bounds = outline.getBounds();
measuredWidth = bounds.getWidth();
measuredHeight = bounds.getHeight();
//assume that each of the width and the height of the string
//is proportional to the font size, calculate the ratio
//and extrapolate linearly to determine the needed font size.
//should have 2 font sizes one for matching the width, and one for
//matching the height, return the least of the 2
widthBasedFontSize = (baseFontSize*width)/measuredWidth;
heightBasedFontSize = (baseFontSize*height)/measuredHeight;
if(widthBasedFontSize < heightBasedFontSize)
{
return base.deriveFont(base.getStyle(), (float)widthBasedFontSize);
}
else
{
return base.deriveFont(base.getStyle(), (float)heightBasedFontSize);
}
}
}
多年来,我曾多次遇到调整文本大小以适应 Java GUI 上特定区域的问题。我的解决方案通常是通过以下方式解决问题:
重新设计界面以避免问题
更改区域以适应文本大小
对正确大小的字体进行二进制搜索以适合字符串(当我无法执行前两个操作时)
最近在做另一个需要快速确定给定区域的正确字体大小的项目时,我的二进制搜索方法太慢了(我怀疑是因为动态内存分配涉及多次创建和测量字体按顺序)并在我的应用程序中引入了明显的滞后。我需要的是一种更快更简单的方法来计算字体大小,允许呈现给定的字符串以适合 GUI 的定义区域。
最后我想到有一种更简单、更快捷的方法,只需要在 运行 时间进行几次分配。这种新方法消除了对任何类型搜索的需要,只需要进行一次测量,但是,它确实需要做出一个假设,该假设对于大多数应用来说都是完全合理的。
- 字体的宽度和高度必须与字体的磅值成正比。除了对渲染上下文所做的最模糊的转换外,这都会发生。
使用这个假设,我们可以计算字体尺寸与点大小的比率,并线性外推以找到给定区域所需的字体大小。我为此编写的一些代码如下:
编辑: 初始测量的准确性受基本字体大小的限制。使用非常小的字体作为基础可能会影响结果。但是base font越大越准确线性逼近越准确
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
public class FontUtilities
{
public static Font createFontToFit
(
String value,
double width,
double height,
Font base,
Graphics context
)
{
double measuredWidth;
double measuredHeight;
double baseFontSize;
FontMetrics ruler;
Rectangle2D bounds;
double heightBasedFontSize;
double widthBasedFontSize;
GlyphVector vector;
Shape outline;
if
(
(value == null) ||
(base == null) ||
(context == null) ||
(width != width) ||
(height != height)
)
{
return null;
}
//measure the size of the string in the current font size
baseFontSize = base.getSize2D();
ruler = context.getFontMetrics(base);
vector = base.createGlyphVector(ruler.getFontRenderContext(), value);
//use the bounds measurement on the outline of the text since this is the only
//measurement method that seems to be bug free and consistent in java
outline = vector.getOutline(0, 0);
bounds = outline.getBounds();
measuredWidth = bounds.getWidth();
measuredHeight = bounds.getHeight();
//assume that each of the width and the height of the string
//is proportional to the font size, calculate the ratio
//and extrapolate linearly to determine the needed font size.
//should have 2 font sizes one for matching the width, and one for
//matching the height, return the least of the 2
widthBasedFontSize = (baseFontSize*width)/measuredWidth;
heightBasedFontSize = (baseFontSize*height)/measuredHeight;
if(widthBasedFontSize < heightBasedFontSize)
{
return base.deriveFont(base.getStyle(), (float)widthBasedFontSize);
}
else
{
return base.deriveFont(base.getStyle(), (float)heightBasedFontSize);
}
}
}