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

如何在Java的JTextArea中为特定字符串添加Tooltip?

问题描述

需求:用户选中JTextArea内已输入的文本,可为其添加Tooltip,鼠标悬停该文本时显示提示信息。
当前问题:添加的Tooltip会在鼠标悬停JTextArea任意区域时显示,而非仅在目标字符串上显示。

问题代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Map;

public class toolTip extends JFrame {
    private JTextArea textArea;
    private JPopupMenu popupMenu;
    private TooltipManager tooltipManager;

    public toolTip() {
        setTitle("Custom Tooltip Application");
        setSize(400, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        textArea = new JTextArea();
        tooltipManager = new TooltipManager(textArea);

        popupMenu = new JPopupMenu();
        JMenuItem addTooltipItem = new JMenuItem("Add Tooltip");
        addTooltipItem.addActionListener(e -> addTooltip());
        popupMenu.add(addTooltipItem);

        textArea.setComponentPopupMenu(popupMenu);

        add(new JScrollPane(textArea), BorderLayout.CENTER);
    }

    private void addTooltip() {
        String selectedText = textArea.getSelectedText();
        if (selectedText != null && !selectedText.isEmpty()) {
            String tooltip = JOptionPane.showInputDialog("Enter tooltip text:");
            if (tooltip != null && !tooltip.isEmpty()) {
                tooltipManager.addTooltip(selectedText, tooltip);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new toolTip().setVisible(true));
    }
}

class TooltipManager {
    private JTextArea textArea;
    private Map<String, String> tooltips = new HashMap<>();

    public TooltipManager(JTextArea textArea) {
        this.textArea = textArea;
        textArea.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                updateTooltip(e);
            }
        });
    }

    public void addTooltip(String text, String tooltip) {
        tooltips.put(text, tooltip);
    }

    private void updateTooltip(MouseEvent e) {
        String word = getWordAtPoint(e.getPoint());
        if (word != null && tooltips.containsKey(word)) {
            textArea.setToolTipText(tooltips.get(word));
        } else {
            textArea.setToolTipText(null);
        }
    }

    private String getWordAtPoint(Point p) {
        int pos = textArea.viewToModel2D(p);
        String text = textArea.getText();
        int start = findWordStart(text, pos);
        int end = findWordEnd(text, pos);
        return text.substring(start, end);
    }

    private int findWordStart(String text, int pos) {
        while (pos > 0 && Character.isLetterOrDigit(text.charAt(pos - 1))) {
            pos--;
        }
        return pos;
    }

    private int findWordEnd(String text, int pos) {
        while (pos < text.length() && Character.isLetterOrDigit(text.charAt(pos))) {
            pos++;
        }
        return pos;
    }
}
问题根源
  1. 匹配逻辑局限性:原代码通过字符串内容作为key存储Tooltip,但getWordAtPoint仅能提取字母/数字组成的"单词",若用户选中的文本包含非字母数字(如空格、符号),或鼠标悬停空白区域时,提取的内容与设置Tooltip的字符串不匹配,易引发异常显示。
  2. 组件级Tooltip的延迟问题:textArea.setToolTipText是给整个JTextArea组件设置Tooltip,Swing的Tooltip管理机制存在延迟,即使后续设置为null,仍可能短时间内显示旧内容。
解决方案

核心思路是记录Tooltip对应的文本区间(起始/结束索引),而非仅依赖字符串内容,精准判断鼠标位置是否落在目标区间内。

修改后的完整代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;

public class toolTip extends JFrame {
    private JTextArea textArea;
    private JPopupMenu popupMenu;
    private TooltipManager tooltipManager;

    public toolTip() {
        setTitle("自定义Tooltip应用");
        setSize(400, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        textArea = new JTextArea();
        tooltipManager = new TooltipManager(textArea);

        popupMenu = new JPopupMenu();
        JMenuItem addTooltipItem = new JMenuItem("添加Tooltip");
        addTooltipItem.addActionListener(e -> addTooltip());
        popupMenu.add(addTooltipItem);

        textArea.setComponentPopupMenu(popupMenu);

        add(new JScrollPane(textArea), BorderLayout.CENTER);
    }

    private void addTooltip() {
        int start = textArea.getSelectionStart();
        int end = textArea.getSelectionEnd();
        if (start != end) { // 确保有选中内容
            String tooltip = JOptionPane.showInputDialog("输入提示文本:");
            if (tooltip != null && !tooltip.isEmpty()) {
                tooltipManager.addTooltip(start, end, tooltip);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new toolTip().setVisible(true));
    }
}

class TooltipManager {
    private JTextArea textArea;
    // 存储每个Tooltip的文本区间和对应提示内容
    private List<TooltipEntry> tooltips = new ArrayList<>();

    static class TooltipEntry {
        int startIndex;
        int endIndex;
        String tooltipText;

        TooltipEntry(int start, int end, String text) {
            this.startIndex = start;
            this.endIndex = end;
            this.tooltipText = text;
        }
    }

    public TooltipManager(JTextArea textArea) {
        this.textArea = textArea;
        // 监听鼠标移动事件,实时更新Tooltip
        textArea.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                updateTooltip(e);
            }
        });
        // 鼠标离开文本区域时清空Tooltip
        textArea.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseExited(MouseEvent e) {
                textArea.setToolTipText(null);
            }
        });
    }

    public void addTooltip(int start, int end, String tooltip) {
        tooltips.add(new TooltipEntry(start, end, tooltip));
    }

    private void updateTooltip(MouseEvent e) {
        Point mousePos = e.getPoint();
        int textPos = textArea.viewToModel2D(mousePos);

        // 遍历所有Tooltip区间,判断当前位置是否在目标范围内
        for (TooltipEntry entry : tooltips) {
            if (textPos >= entry.startIndex && textPos <= entry.endIndex) {
                textArea.setToolTipText(entry.tooltipText);
                return;
            }
        }
        // 不在任何目标区间,清空Tooltip
        textArea.setToolTipText(null);
    }
}
修改说明
  • 改用区间存储:通过记录选中文本的起始和结束索引,避免相同字符串重复导致的匹配错误,同时支持任意内容的文本(包括含非字母数字的片段)。
  • 精准位置判断:鼠标移动时,将屏幕坐标转换为文本索引,直接判断是否落在某个Tooltip区间内,确保只有悬停目标文本时才显示提示。
  • 优化Tooltip生命周期:添加鼠标退出事件,确保离开文本区域时Tooltip立即消失,避免残留显示。

内容的提问来源于stack exchange,提问作者Nicol64PA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:28:11