java - 文本中的 Calibri 字体移动到组件的底部
问题描述
没有太多要解释的。只需查看下面的 MCVE/图像:
public class FontExample extends JFrame {
private static final Font FONT = new Font("Calibri", Font.PLAIN, 14);
public FontExample() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
JLabel withoutHtml = new JLabel("hello stackoverflow");
withoutHtml.setFont(FONT);
withoutHtml.setBorder(BorderFactory.createLineBorder(Color.red));
add(withoutHtml);
JLabel withHtml = new JLabel("<html><body style='vertical-align:top;'>hello stackoverflow");
withHtml.setBorder(BorderFactory.createLineBorder(Color.green));
withHtml.setFont(FONT);
add(withHtml);
setLocationByPlatform(true);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
//Make sure Calibri font is installed
if (!"Calibri".equals(FONT.getFamily())) {
System.err.println("Font calibri is not installed.");
System.exit(1);
}
new FontExample().setVisible(true);
});
}
}
绿色的是带有<html>
标签的。有没有办法解决它?通过修复,我的意思是让它像左边的一样,没有这个愚蠢的空间?
任何其他字体似乎都不会发生这种情况(我又测试了 2-3 个)。我在 Java 8 上使用 Windows 7 和 Windows 10。
我试图在底部添加填充:
JLabel withHtml = new JLabel("<html><body style='padding-bottom:5px'>hello stackoverflow");
正如预期的那样,我得到的是:
其中a)将拧紧同一容器中其他组件的对齐方式(不利于UI目的)和b)我将不得不硬编码自5以来的很多值,因为它适合字体大小14。但对于其他字体大小,它需要另一个值。
@Andrew Thomson 在评论中说对所有 JLabel 使用 HTML 格式。但是,如果它们位于另一个基于文本的组件(如 a )旁边JTextField
,我会得到以下信息:
这显然也很糟糕。
更新
此外,我尝试从网络的某处下载 Calibri 字体(以及“Calibri Light”等变体)并按照此问题中的描述进行安装。我不知道这是否会“覆盖”现有的,但我得到了相同的结果。
解决方案
一行文本由 3 个部分组成:
为了看得更清楚,我使用了大小为 50 的 Calibri。没有 HTML 的标签是:
在 HTML 模式下,情况有所不同。HTML 渲染器将前导放在首位(出于某种原因):
这会产生您观察到的令人不快的结果。
现在你会问“但为什么我只看到 Calibri 的效果?” 事实上,所有字体都存在这种效果,但它通常要小得多,所以你不会注意到它。
这是一个输出一些常见 Windows 字体指标的程序:
import java.awt.*;
import javax.swing.JLabel;
public class FontInfo
{
static void info(String family, int size)
{
Font font = new Font(family, Font.PLAIN, size);
if(!font.getFamily().equals(family))
throw new RuntimeException("Font not available: "+family);
FontMetrics fm = new JLabel().getFontMetrics(font);
System.out.printf("%-16s %2d %2d %2d\n", family, fm.getAscent(), fm.getDescent(), fm.getLeading());
}
public static void main(String[] args)
{
String[] fonts = {"Arial", "Calibri", "Courier New", "Segoe UI", "Tahoma", "Times New Roman", "Verdana"};
System.out.printf("%-16s %s\n", "", " A D L");
for(String f : fonts)
info(f, 50);
}
}
对于尺寸 50,结果为:
日常活动 宋体 46 11 2 口径 38 13 11 快递新 42 15 0 Segoe UI 54 13 0 塔霍马 50 11 0 时代新罗马 45 11 2 维达纳 51 11 0
如您所见,与其他字体相比,Calibri 的领先优势很大。
对于尺寸 14,结果为:
日常活动 宋体 13 3 1 口径 11 4 3 快递 新 12 5 0 Segoe 用户界面 16 4 0 塔霍马 14 3 0 时代新罗马 13 3 1 维达纳 15 3 0
Calibri 的领先仍然是 3 像素。其他字体有 0 或 1,这意味着它们的效果是不可见的或非常小。
似乎不可能改变 HTML 渲染器的行为。但是,如果目标是对齐相邻组件的基线,那么这是可能的。您使用的FlowLayout
有一个属alignOnBaseline
性。如果启用它,它会正确对齐组件:
更新 1
这是一个JFixedLabel
给出相同结果的类,无论它包含 HTML 还是纯文本。它Graphics
在 HTML 模式下按前导值转换:
import java.awt.Graphics;
import javax.swing.JLabel;
import javax.swing.plaf.basic.BasicHTML;
public class JFixedLabel extends JLabel
{
public JFixedLabel(String text)
{
super(text);
}
@Override
protected void paintComponent(Graphics g)
{
int dy;
if(getClientProperty(BasicHTML.propertyKey)!=null)
dy = getFontMetrics(getFont()).getLeading();
else
dy = 0;
g.translate(0, -dy);
super.paintComponent(g);
g.translate(0, dy);
}
}
结果:
更新 2
之前的解决方案在图标方面存在问题,所以这里有一个新的解决方案,可以同时处理文本和图标。这里我们不扩展JLabel
,而是定义一个新的 UI 类:
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.metal.MetalLabelUI;
public class FixedLabelUI extends MetalLabelUI
{
@Override
protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text, Icon icon,
Rectangle viewR, Rectangle iconR, Rectangle textR)
{
String res = super.layoutCL(label, fontMetrics, text, icon, viewR, iconR, textR);
if(label.getClientProperty(BasicHTML.propertyKey)!=null)
textR.y -= fontMetrics.getLeading();
return res;
}
}
要将 UI 分配给标签,请执行以下操作:
JLabel label = new JLabel();
label.setUI(new FixedLabelUI());
推荐阅读
- swift - 我添加了新的 CategoryViewController.swift,但是当我将新的 tableview 链接到类时,我在自定义类中找不到它
- android - 从服务器获取数据后聊天消息(回收站视图)闪烁?
- python - 无法在烧瓶中正确提供静态文件
- javascript - 如何创建编辑按钮在 CRUD 中正常工作?
- python - 是否可以将多个变量传递给未在调用它的前一个函数中本地声明的函数(或全局声明)?
- android - MVVM Android MediatorLiveData 嵌套调用
- pandas - PANDAS:一种组合按字段分组的行的方法
- sql - 使用 ON 子句连接具有相同列名的表
- python - pandas.DataFrame.groupby 省略列
- python - Pandas - 基于与另一列的交叉引用计算新值