You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为显示回显字符的JPasswordField设置TextAttribute.TRACKING字间距

问题根因

你遇到的问题是Swing默认JPasswordField的UI实现逻辑导致的:当设置了非0的回显字符时,默认的密码框UI类在绘制回显圆点时,不会应用你给字体设置的TextAttribute.TRACKING字间距属性,而是固定按照默认字符宽度逐个绘制回显符号,只有当你设置回显字符为0显示明文时,才会走普通JTextField的文本绘制逻辑,应用字体的字间距属性。该问题和具体外观实现无关,Metal、Windows等主流外观都遵循这套逻辑。

可行解决方案

我们可以通过自定义BasicPasswordFieldUI的子类,重写回显字符的绘制逻辑,让绘制时主动应用字间距设置,以下是完整可运行的修改后代码:

import javax.swing.*;
import javax.swing.plaf.basic.BasicPasswordFieldUI;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Map;

public class PasswordFieldLetterSpacingFix {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JPasswordField field = new JPasswordField(15);
            // 应用自定义UI解决回显时字间距不生效问题
            field.setUI(new SpacingPasswordUI());
            Map<TextAttribute, Object> attributes = new HashMap<>();
            attributes.put(TextAttribute.TRACKING, 0.5);
            field.setFont(field.getFont().deriveFont(attributes));

            JLabel showPassLabel = new JLabel("move mouse here");
            showPassLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
            showPassLabel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent ev) {
                    field.setEchoChar((char) 0);
                }

                @Override
                public void mouseExited(MouseEvent ev) {
                    field.setEchoChar((char) UIManager.get("PasswordField.echoChar"));
                }
            });

            JFrame frame = new JFrame("密码框字间距修复");
            frame.setLayout(new FlowLayout());

            frame.add(field);
            frame.add(showPassLabel);

            frame.pack();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }

    // 自定义支持字间距的密码框UI
    static class SpacingPasswordUI extends BasicPasswordFieldUI {
        @Override
        protected void paintSafely(Graphics g) {
            JPasswordField passwordField = (JPasswordField) getComponent();
            // 明文模式走默认绘制逻辑
            if (passwordField.getEchoChar() == 0) {
                super.paintSafely(g);
                return;
            }
            // 回显模式自定义绘制,主动应用字间距
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(passwordField.getForeground());
            g2d.setFont(passwordField.getFont());
            FontMetrics fm = g2d.getFontMetrics();
            char echoChar = passwordField.getEchoChar();
            int dotWidth = fm.charWidth(echoChar);
            // 读取字体设置的tracking值计算额外字间距
            float tracking = (Float) passwordField.getFont().getAttributes().getOrDefault(TextAttribute.TRACKING, 0f);
            int extraSpacing = (int) (dotWidth * tracking);
            int x = passwordField.getInsets().left;
            int y = passwordField.getInsets().top + fm.getAscent();
            int charCount = passwordField.getPassword().length;
            for (int i = 0; i < charCount; i++) {
                g2d.drawString(String.valueOf(echoChar), x, y);
                x += dotWidth + extraSpacing;
            }
            g2d.dispose();
        }
    }
}
注意事项
  • 该实现兼容所有主流Swing外观,无需额外适配
  • 回显模式和明文模式的字间距会自动和你设置的TextAttribute.TRACKING值保持一致
  • 如果需要全局生效,可以通过UIManager.put("PasswordFieldUI", "你的类全路径.SpacingPasswordUI")设置密码框默认UI

内容的提问来源于stack exchange,提问作者George Z.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 09:36:01