【问题标题】:Text-mouseover popups over a Swing JTextArea?Swing JTextArea 上的文本鼠标悬停弹出窗口?
【发布时间】:2011-08-22 20:52:17
【问题描述】:

有什么东西可以让您在Swing JTextArea 中的单个单词 或字母上显示一个小的文本弹出窗口(如工具提示)? (或具有类似功能的 JTextArea 替代品。)

我需要的应该像一个工具提示,换句话说,只有在鼠标悬停在单词上一两秒后才会显示弹出文本,一旦鼠标移开它就会自动消失。当然,这里棘手的部分是我希望它在文本中的字符/单词级别,而不是在组件级别......有什么建议吗?

【问题讨论】:

  • @trashgod 和@camickr 都说了 :-) Swing 本身唯一有点讨厌:实现与位置相关的工具提示需要子类化。与滚动您自己的 WhatHoverManager 相比,这是一个很小的代价

标签: java swing jtextarea


【解决方案1】:

这是一个基于@trashgods 和@camickr 答案的实际实现:

addToolTip(line,toolTip) 

您可以为特定行添加工具提示,当您将鼠标悬停在该行上时会显示该工具提示。您可能需要设置 debug=true 以获取每个位置的工具提示显示。

如果您想为没有特定行的行显示一般工具提示,您可能需要使用

添加它
addToolTip(-1,"general tool tip").

这个解决方案不是 100% 的要求,但非常接近。通过一些调整,它应该得到最初想要的结果。

源代码:

package com.bitplan.swingutil;

import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JTextArea;
import javax.swing.text.BadLocationException;

/**
 * Answer for 
 * http://stackoverflow.com/questions/5957241/text-mouseover-popups-over-a-swing-jtextarea/35250911#35250911
 * 
 * see http://stackoverflow.com/a/35250911/1497139
 * a JTextArea that shows the current Position of the mouse as a tooltip
 * @author wf
 *
 */
public class JToolTipEventTextArea extends JTextArea {
  // make sure Eclipse doesn't show a warning
  private static final long serialVersionUID = 1L;

  // switch to display debugging tooltip
  boolean debug=false;

  /**
   * the map of tool tips per line
   */
  public Map<Integer,String> lineToolTips=new HashMap<Integer,String>();

  /**
   * create me with the given rows and columns
   * @param rows
   * @param cols
   */
  public JToolTipEventTextArea(int rows, int cols) {
    super(rows,cols);
    // initialize the tool tip event handling
    this.setToolTipText("");
  }

  /**
   * add a tool tip for the given line
   * @param line - the line number
   * @param tooltip - 
   */
  public void addToolTip(int line,String tooltip) {
    lineToolTips.put(line,tooltip);
  }

  /**
   * get the ToolTipText for the given mouse event
   * @param event - the mouse event to handle
   */
  public String getToolTipText(MouseEvent event) {
    // convert the mouse position to a model position
    int viewToModel =viewToModel(event.getPoint());
    // use -1 if we do not find a line number later
    int lineNo=-1;
    // debug information
    String line=" line ?";
    // did we get a valid view to model position?
    if(viewToModel != -1){
      try {
        // convert the modelPosition to a line number
        lineNo = this.getLineOfOffset(viewToModel)+1;
        // set the debug info
        line=" line "+lineNo;
      } catch (BadLocationException ble) {
        // in case the line number is invalid ignore this 
        // in debug mode show the issue 
        line=ble.getMessage();
      }
    }
    // try to lookup the tool tip - will be null if the line number is invalid
    // if you want to show a general tool tip for invalid lines you might want to
    // add it with addToolTip(-1,"general tool tip")
    String toolTip=this.lineToolTips.get(lineNo);
    // if in debug mode show some info
    if (debug)  {
      // different display whether we found a tooltip or not
      if (toolTip==null) {
        toolTip="no tooltip for line "+lineNo;
      } else {
        toolTip="tooltip: "+toolTip+" for line "+lineNo;
      }
      // generally we add the position info for debugging
      toolTip+=String.format(" at %3d / %3d ",event.getX(),event.getY());
    } 
    // now return the tool tip as wanted
    return toolTip;
  }
}

【讨论】:

    【解决方案2】:

    您可以根据需要覆盖getToolTipText(Mouse Event event)

    附录:JTextComponentJTextArea 的父级通过两种方法提供位置信息:modelToView()viewToModel()latter 应该让您将鼠标位置转换为文档偏移量。

    【讨论】:

    • @trashgod +1,但我知道来自 Renderer 的 someComponet.dispatchEvent(SwingUtilities.convertMouseEvent(..) 但如何处理 TextArea + Caret
    • @mKorbel:问得好。我认为JTextComponent 中的位置信息就足够了。作为参考,我更新了JTextComponent#getToolTipText() 的链接,它覆盖了JComponent 中的链接。
    • @mKorbel .. 什么意思?通常,我理解你的话(一个接一个),但不理解它们的组合;-)
    • @trashgod 我有类似的问题,我在JTextArea 中覆盖了getToolTipText(MouseEvent event),但它没有被调用。有什么建议吗?
    • @user3111525:使用默认值调用setToolTipText()以初始化ToolTipManager
    【解决方案3】:

    当然,这里棘手的部分是我希望它在文本中的字符/单词级别

    您使用鼠标点来确定您在文本区域中的位置:

    int offset = textArea.viewToModel(...);
    

    现在您有了偏移量,您可以在该位置获取字符或单词。 Utilities 类具有 getWordStart() 和 getWordEnd() 等方法。

    然后你使用 getText(...) 方法来获取单词或字符。

    【讨论】:

    • +1 感谢您对此进行验证。当我对@mKorbel 发表评论时,我不确定,当我更新mine 时,我无意中忽略了您的回答。
    【解决方案4】:

    也许

    import java.awt.*;
    import java.awt.event.*;
    import java.awt.font.*;
    import java.awt.geom.*;
    import javax.swing.*;
    import java.util.*;
    import javax.swing.event.*;
    
    public class SimplePaintSurface implements Runnable, ActionListener {
    
        private static final int WIDTH = 1250;
        private static final int HEIGHT = 800;
        private Random random = new Random();
        private JFrame frame = new JFrame("SimplePaintSurface");
        private JPanel tableaux;
    
        @Override
        public void run() {
            tableaux = new JPanel(null);
            for (int i = 1500; --i >= 0;) {
                addRandom();
            }
            frame.add(tableaux, BorderLayout.CENTER);
            JButton add = new JButton("Add");
            add.addActionListener(this);
            frame.add(add, BorderLayout.SOUTH);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(WIDTH, HEIGHT);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            tableaux.requestFocusInWindow();
        }
    
        @Override
        public void actionPerformed(final ActionEvent e) {
            addRandom();
            tableaux.repaint();
        }
    
        void addRandom() {
            Letter letter = new Letter(Character.toString((char) ('a' + random.nextInt(26))));
            letter.setBounds(random.nextInt(WIDTH), random.nextInt(HEIGHT), 16, 16);
            tableaux.add(letter);
        }
    
        public static void main(final String[] args) {
            SwingUtilities.invokeLater(new SimplePaintSurface());
        }
    }
    
    class Letter extends JLabel {
    
        private Font font1;
        private Font font2;
        private final FontRenderContext fontRenderContext1;
        private final FontRenderContext fontRenderContext2;
    
        public Letter(final String letter) {
            super(letter);
            setFocusable(true);
            setBackground(Color.RED);
            font1 = getFont();
            font2 = font1.deriveFont(48f);
            fontRenderContext1 = getFontMetrics(font1).getFontRenderContext();
            fontRenderContext2 = getFontMetrics(font2).getFontRenderContext();
            MouseInputAdapter mouseHandler = new MouseInputAdapter() {
    
                @Override
                public void mouseEntered(final MouseEvent e) {
                    Letter.this.setOpaque(true);
                    setFont(font2);
                    Rectangle bounds = getBounds();
                    Rectangle2D stringBounds = font2.getStringBounds(getText(), fontRenderContext2);
                    bounds.width = (int) stringBounds.getWidth();
                    bounds.height = (int) stringBounds.getHeight();
                    setBounds(bounds);
                }
    
                @Override
                public void mouseExited(final MouseEvent e) {
                    Letter.this.setOpaque(false);
                    setFont(font1);
                    Rectangle bounds = getBounds();
                    Rectangle2D stringBounds = font1.getStringBounds(getText(), fontRenderContext1);
                    bounds.width = (int) stringBounds.getWidth();
                    bounds.height = (int) stringBounds.getHeight();
                    setBounds(bounds);
                }
            };
            addMouseListener(mouseHandler);
        }
    }
    

    【讨论】:

      【解决方案5】:

      这听起来很棘手。这只是我的想法,可能不会被投票。但你也许可以做到这一点。

      假设有某种 HoverListener 或您可以实现的东西,否则您将不得不实现鼠标侦听器并构建自己的等待计时器。但是一旦你到了这里,你知道你想弹出一个工具提示,你就是不知道他们在哪个字母/单词上。我很确定可以在屏幕上获取光标位置。然后使用这个位置,您可能能够计算光标在 TextArea 内的位置,然后您可以抓取字符。一旦你有了角色/位置,你也可以不用更多的工作就可以抓住整个单词。 (请注意,在计算光标悬停的内容时,您希望获取文本区域的“视口”,如果您的文本区域有滚动条,则视口将仅代表屏幕上可见的区域)

      很抱歉,答案很冗长,但如果我必须拥有此功能并且我知道 Swing 不提供它,我会尝试这样做的一般逻辑。希望这是一个不错的起点。

      【讨论】:

      • 您必须将 MouseMotionListener 添加到 JTextArea。但 getToolTipText 要简单得多。
      • 覆盖 toolTipText 更简单,但我认为使用 MouseMotionListener 可能会在其事件参数中传递光标的位置,但 toolTipText 也可以。只是看看那里有什么,我猜有什么可用的。就像我说的,我对任何选票都不抱希望,但我想至少提供一些东西来激发其他人的创造力。
      • 也许它会被投票,向下......创造力很好,但浪费在简单地重新发明轮子上。只有当你在一天结束时拿出一个令人难以置信的增强轮时,你才会得到赞誉。成功的先决条件是对已经存在的简单轮子及其局限性(如果有的话)有深入的了解……仅仅假设是没有帮助的。布道结束:-)
      猜你喜欢
      • 1970-01-01
      • 2022-12-19
      • 2015-12-11
      • 1970-01-01
      • 2010-11-02
      • 2016-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多