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

Java 8下解决Windows L&F中Swing文本高亮后可读性差的问题

Java 8下解决Windows L&F中Swing文本高亮后可读性差的问题

我完全懂你遇到的这个糟心问题——Windows外观风格下,选中文本的默认背景是白色,要是这段文本还加了黄色高亮,那可读性直接拉胯。核心问题就是Swing原本的前景色逻辑只会盯着组件的默认背景属性(比如你说的蓝色)来计算,完全不管当前绘制的文本区域实际是高亮状态还是选中状态,更没法拿到实际的背景颜色来调整。好在Java 8下有个靠谱的解决方案,咱们来一步步搞定它。

解决思路

要解决这个问题,我们得绕开Swing默认的高亮绘制逻辑,自己写一个智能高亮绘制器:

  1. 这个绘制器不仅要能画出原来的黄色高亮背景,还要能判断当前绘制的文本区域是否被选中;
  2. 针对不同的背景(普通高亮黄/Windows选中白),用亮度算法计算出最合适的前景色(浅背景配黑字,深背景配白字,保证足够的对比度);
  3. 在绘制文本时临时替换前景色,让高亮/选中区域的文本自动适配背景,彻底解决可读性问题。

完整修改后的代码

package demos.text;

import com.sun.java.swing.plaf.windows.WindowsLookAndFeel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.View;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;

public class HighlighterDemo {
    // 替换成自定义的智能高亮绘制器
    private static final SmartHighlightPainter painter = new SmartHighlightPainter(Color.YELLOW);

    public static void main(String[] args) throws BadLocationException, UnsupportedLookAndFeelException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        UIManager.setLookAndFeel(WindowsLookAndFeel.class.getName());
        Container mainPanel = createMainPanel();
        JFrame frame = new JFrame("Highlighter Demo");
        frame.setContentPane(mainPanel);
        frame.setLocationRelativeTo(null);
        frame.pack();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    private static JPanel createMainPanel() throws BadLocationException {
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(createTextArea());
        return panel;
    }

    private static JTextArea createTextArea() throws BadLocationException {
        JTextArea textArea = new JTextArea();
        textArea.setFont(new Font("Arial", Font.PLAIN, 20));
        String text = "Some sample text";
        textArea.setText(text);
        // 应用自定义高亮绘制器
        textArea.getHighlighter().addHighlight(text.indexOf("sample"), text.indexOf("text"), painter);
        return textArea;
    }

    /**
     * 根据背景色计算最佳前景色(遵循WCAG国际亮度标准,保证对比度)
     */
    private static Color getOptimalForeground(Color background) {
        // 计算相对亮度:(0.299*红 + 0.587*绿 + 0.114*蓝) / 255
        double luminance = (0.299 * background.getRed() + 0.587 * background.getGreen() + 0.114 * background.getBlue()) / 255;
        // 浅背景(亮度>0.5)用黑色,深背景用白色
        return luminance > 0.5 ? Color.BLACK : Color.WHITE;
    }

    /**
     * 智能高亮绘制器:自动适配前景色的核心类
     */
    static class SmartHighlightPainter implements Highlighter.LayerPainter {
        private final Color highlightColor;

        public SmartHighlightPainter(Color highlightColor) {
            this.highlightColor = highlightColor;
        }

        @Override
        public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
            // 完全复刻DefaultHighlightPainter的背景绘制逻辑,保证高亮样式和原来一致
            Rectangle alloc = null;
            if (offs0 == offs1) {
                try {
                    Shape shape = c.modelToView(offs0);
                    alloc = shape.getBounds();
                } catch (BadLocationException e) {
                    return;
                }
                g.drawLine(alloc.x, alloc.y, alloc.x, alloc.y + alloc.height);
                return;
            }
            if (offs0 > offs1) {
                int temp = offs0;
                offs0 = offs1;
                offs1 = temp;
            }
            try {
                Shape shape = c.modelToView(offs0);
                alloc = shape.getBounds();
                if (offs1 == c.getDocument().getLength()) {
                    int endX = c.getWidth();
                    g.setColor(highlightColor);
                    g.fillRect(alloc.x, alloc.y, endX - alloc.x, alloc.height);
                } else {
                    Shape s = c.modelToView(offs1);
                    Rectangle rect = s.getBounds();
                    g.setColor(highlightColor);
                    g.fillRect(alloc.x, alloc.y, rect.x - alloc.x, alloc.height);
                }
            } catch (BadLocationException e) {
                // 忽略绘制异常,不影响整体功能
            }
        }

        @Override
        public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
            Color currentBg = highlightColor;

            // 判断当前高亮区域是否和选中区域重叠
            if (c.getSelectionStart() != c.getSelectionEnd()) {
                int selStart = c.getSelectionStart();
                int selEnd = c.getSelectionEnd();
                // 检查两个区间是否有交集
                if (!(offs1 <= selStart || offs0 >= selEnd)) {
                    // 重叠区域使用Windows L&F的系统选中背景色
                    currentBg = UIManager.getColor("TextArea.selectionBackground");
                    if (currentBg == null) {
                        currentBg = Color.WHITE; // 兜底默认值,防止获取失败
                    }
                }
            }

            // 计算最佳前景色并临时应用到绘制上下文
            Color optimalFg = getOptimalForeground(currentBg);
            Color originalFg = g.getColor();
            g.setColor(optimalFg);

            // 让View用新的前景色绘制文本
            try {
                Shape drawBounds = view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
                view.paint(g, drawBounds);
                return drawBounds;
            } catch (BadLocationException e) {
                return bounds;
            } finally {
                // 恢复原来的颜色,避免影响其他区域的文本绘制
                g.setColor(originalFg);
            }
        }
    }
}

关键细节说明

  1. 亮度计算逻辑:用WCAG国际标准的相对亮度公式,比简单取RGB平均值更精准,能保证文本和背景的对比度符合无障碍设计要求;
  2. 智能背景判断:自动识别当前区域是否被选中,优先使用系统的选中背景色计算前景色,完全贴合Windows L&F的风格;
  3. 兼容性保障:完全适配Java 8,绘制背景的逻辑和DefaultHighlightPainter完全一致,不会破坏原来的高亮样式,只是在文本绘制环节动态调整了前景色。

这样修改后,不管是普通的黄色高亮文本,还是被选中的黄色高亮文本,都会自动切换到最适合的前景色,彻底解决可读性差的问题。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 03:10:05