将 JLabel 放置在鼠标下方?

Get a JLabel to be positioned under mouse?

我正在制作鼠标事件侦听器和处理程序。我有这样的东西:

JLabel status = new JLabel("I can change");

并使用我的 MouseListener,我正在根据给定条件更改 JLabel 的文本。我可以很容易地将这个 JLabel 放在一个地方,例如 window 的底部,像这样:

add(status, BorderLayout.SOUTH);

但我想做的是获取那个 JLabel,并直接在鼠标下方显示它。这类似于

.setToolTipText

方法,但我无法使用它,因为我将鼠标移到 JDialog 中的图像上,该图像采用网格布局格式。请解释如何将不断变化的 JLabel 直接放在鼠标下,就像 .setToolTipText 一样。

在 JDialog 中的 GridLayout 中有两个图像:

    JDialog giraffewindow = new JDialog();
            Icon giraffe = new ImageIcon(getClass().getResource("giraffe.png"));
            Icon windows = new ImageIcon(getClass().getResource("windows.png"));

            giraffewindow.setLayout(new GridLayout(1, 2, 0, 0));
            giraffewindow.add(new JLabel (windows));
            giraffewindow.add(new JLabel (giraffe));

非常感谢您抽出宝贵时间阅读本文,我非常感谢您为帮助其他程序员所做的努力!

为此,您需要将 JLabel 的父容器(JDialog)的布局设置为绝对布局。这将允许您手动设置标签的位置。

要将布局设置为空:

myPanel.setLayout(null);

然后,要设置标签的位置,请调用 setBounds 方法:

status.setBounds(int xPos, int yPos, int width, int height);

在您的情况下,只要移动鼠标,您就有可能 xPosyPos 到鼠标的坐标。如果您需要一点偏移,您可能还想向坐标添加一些常量。

例如:

    giraffewindow.add(status);

    giraffewindow.addMouseMotionListener(new MouseMotionListener() {

        @Override
        public void mouseMoved(MouseEvent evt) {
            status.setBounds(evt.getX(), evt.getY(), labelWidth,
                    labelHeight);
        }

        @Override
        public void mouseDragged(MouseEvent arg0) {}
    });

编辑:

我刚刚了解到仍然可以在非绝对布局上设置边界,例如 GridLayout。我原以为只有绝对布局支持这一点,但快速测试证明我错了。但是,向 GridLayout 添加另一个组件似乎有点混乱。当我解决这个问题时,我会提供另一个更新


编辑 2:

为了JLabel不打乱布局,你有两个选择:

  1. 使用一个GlassPane。将玻璃板想象成覆盖其下方组件的 sheet 玻璃。通过将 JLabel 添加到玻璃窗格而不是 JDialog,我们可以避免弄乱布局。见下文:

    JPanel glass = ((JPanel) giraffewindow.getGlassPane());
    glass.setVisible(true);
    glass.add(status);
    
    glass.addMouseMotionListener(new MouseMotionListener() {
    
        @Override
        public void mouseMoved(MouseEvent evt) {
            status.setBounds(evt.getX(), evt.getY(), labelWidth,
                    labelHeight);
        }
    
        @Override
        public void mouseDragged(MouseEvent arg0) {
        }
    });
    

这种方法的问题在于它会拦截所有鼠标事件,这意味着玻璃下方的 JButton 等不会。一些可能的解决方法包括让玻璃查看每个鼠标事件并决定是否应该将该事件传递给另一个组件。请参阅 here for an example 了解如何使用此概念。

  1. 另一种可能性是 JLayeredPanes。注意:我几乎没有使用 JLayeredPanes 的经验,所以这里和那里可能会有小的错误。基本上,JLayeredPane 允许您将组件层叠在一起。出于您的目的,我将创建两层:一层包含新的 JPanel 和包含图像的 GridLayout,另一层包含 status 移动标签。见下文:

    JLayeredPane layeredPane = new JLayeredPane();
    {
        JPanel imagePanel = new JPanel();
        layeredPane.add(imagePanel);
        layeredPane.setLayer(imagePanel, 0);
        imagePanel.setLayout(new GridLayout(1, 2, 0, 0));
        {
            imagePanel.add(new JLabel (windows));
            imagePanel.add(new JLabel (giraffe));
        }
    
        layeredPane.add(status);
        layeredPane.setLayer(status, 1);
    }
    giraffewindow.add(layeredPane);
    

不幸的是,这也遇到了同样的问题。您可以将状态标签包围在 JPanel 中,并将鼠标侦听器添加到此面板,但鼠标操作将不会传递到下面的任何按钮。或者,您可以简单地将鼠标侦听器添加到主 JDialog,这意味着诅咒捕获鼠标事件的组件(例如 JButton)将阻止 status 标签移动.

but I cannot use that - setToolTipText()

为什么?

I am moving the mouse over an image

您正在将鼠标移到支持工具提示文本的 JLabel 上。

如果您希望工具提示随鼠标一起移动,您只需重写 getToolTipLocation(...) 方法,如下所示:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.UIManager.*;


public class ToolTipPanel extends JPanel
{
    public ToolTipPanel()
    {
        setPreferredSize( new Dimension(200, 200) );
        setToolTipText("");
    }

    public void paintComponent(Graphics g)
    {
        g.setColor( Color.red );
        g.fillRect(0, 0, 100, 200);
        g.setColor( Color.blue );
        g.fillRect(100, 0, 100, 200);
    }

    @Override
    public String getToolTipText(MouseEvent e)
    {
        if (e.getX() < 100)
            return "red";
        else
            return "blue";
    }

    @Override
    public Point getToolTipLocation(MouseEvent e)
    {
        Point p = e.getPoint();
        p.y += 15;

        return p;
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new ToolTipPanel());
        frame.setLocationByPlatform( true );
        frame.pack();
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}

另一种方法是使用 JXLayer 在移动鼠标时绘制文本。查看 How to Decorate Components with the JLayer Class 上的 Swing 教程中的 Responding to Events 示例,作为入门示例。