【问题标题】:How to pop up a text box(or tooltip) via script in Java如何通过 Java 中的脚本弹出文本框(或工具提示)
【发布时间】:2014-05-24 05:10:08
【问题描述】:

我有一个非常具体的问题: 我希望能够通过方法调用在屏幕上的给定位置弹出一个带有文本的工具提示(它可以说任何东西)在方法调用的一定时间内(比如逻辑在一个谈话方法中)和淡去。我该怎么办?有没有办法通过 JTooltip 做到这一点?还是我必须潜入 JNA 才能得到我想要的?

我应该提一下,我希望工具提示在给定位置弹出文本,而不需要鼠标悬停提示,就像弹出窗口一样。

另外,如果工具提示不是实现我想要的东西的正确方法(我希望我已经说清楚了),是否有更有效的替代方法?

【问题讨论】:

    标签: java popup tooltip jna jtooltip


    【解决方案1】:

    实现这一目标的方法很少。一种可能的方法是使用透明的JWindow 和 Swing Timer

    基本上,它的作用是创建一个JWindow,将其背景颜色设置为完全透明,从而形成一个透明窗口。然后它使用简单的BackgroundPane(渲染漂亮的背景)和MessagePane 来保存实际的消息。您可以在一个面板中执行此操作,但我喜欢它为我提供的灵活性。

    现在,就个人而言,我会创建一个更简单的 API,它可以构建弹出窗口并创建一个具有可变延迟的 Timer,但你明白了

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.LinearGradientPaint;
    import java.awt.Point;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.RoundRectangle2D;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JWindow;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.border.EmptyBorder;
    
    public class PopupMessageWindow {
    
        public static void main(String[] args) {
            new PopupMessageWindow();
        }
    
        public PopupMessageWindow() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    final JWindow frame = new JWindow();
                    frame.setBackground(new Color(0, 0, 0, 0));
                    BackgroundPane pane = new BackgroundPane();
                    pane.setMessage("Boo, This is a popup...");
                    frame.add(pane);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                    frame.setAlwaysOnTop(true);
    
                    Timer timer = new Timer(10000, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            frame.dispose();
                            System.exit(0);
                        }
                    });
                    timer.setRepeats(false);
                    timer.start();
                }
            });
        }
    
        public class BackgroundPane extends JPanel {
    
            private MessagePane messagePane;
    
            public BackgroundPane() {
                setBorder(new EmptyBorder(40, 40, 40, 40));
                messagePane = new MessagePane();
                setLayout(new BorderLayout());
                add(messagePane);
                setOpaque(false);
            }
    
            public void setMessage(String msg) {
                messagePane.setMessage(msg);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
                g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                LinearGradientPaint glp = new LinearGradientPaint(
                                new Point(0, 0),
                                new Point(0, getHeight()),
                                new float[]{0f, 1f},
                                new Color[]{Color.GRAY, Color.BLACK});
                RoundRectangle2D frame = new RoundRectangle2D.Float(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
                g2d.setPaint(glp);
                g2d.fill(frame);
                g2d.setColor(Color.WHITE);
                g2d.draw(frame);
            }
    
        }
    
        public class MessagePane extends JPanel {
    
            private JLabel label;
    
            public MessagePane() {
                setOpaque(false);
                label = new JLabel();
                label.setForeground(Color.WHITE);
                setLayout(new GridBagLayout());
                add(label);
            }
    
            public void setMessage(String msg) {
                label.setText(msg);
            }
    
        }
    
    }
    

    您可以在背景面板上使用AlphaComposite 来创建半透明背景

    使用 50% 的弹出窗口AlphaComposite

    更新

    您可以使用工厂模式或构建器模式来提供简单的 API,例如...

    new PopupBuilder().at(new Point(100, 100)).withMessage("Hello").withDelay(5000).show();
    

    构建器将收集您要指定的属性,在您未设置它们的地方提供默认值,然后显示最终弹出窗口。

    基本思想是当您调用show 时,它会收集属性并构建类似于现在构造函数工作方式的窗口...

    更新为淡出弹出窗口

    这是(有点过分)如何产生淡入/淡出效果的示例。该示例保证消息将在指定的延迟时间内(满屏)显示在屏幕上

    import java.awt.AlphaComposite;
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.LinearGradientPaint;
    import java.awt.Point;
    import java.awt.RenderingHints;
    import java.awt.Window;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.RoundRectangle2D;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JWindow;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.border.EmptyBorder;
    
    public class PopupMessageExample {
    
        public static void main(String[] args) {
            new PopupMessageExample();
        }
    
        public PopupMessageExample() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
                    new PopupMessageBuilder().withDelay(10000).withMessage("Hello, this is a fading message").show();
                }
            });
        }
    
        public class PopupMessageBuilder {
    
            private int delay;
            private Point location;
            private String message;
    
            private long startTime;
            private Timer fadeTimer;
    
            public PopupMessageBuilder at(Point p) {
                this.location = p;
                return this;
            }
    
            public PopupMessageBuilder withDelay(int delay) {
                this.delay = delay;
                return this;
            }
    
            public PopupMessageBuilder withMessage(String msg) {
                this.message = msg;
                return this;
            }
    
            public PopupMessageBuilder show() {
    
                final JWindow frame = new JWindow();
                frame.setOpacity(0f);
                frame.setBackground(new Color(0, 0, 0, 0));
                BackgroundPane pane = new BackgroundPane();
                pane.setMessage(message);
                frame.add(pane);
                frame.pack();
                if (location == null) {
                    frame.setLocationRelativeTo(null);
                } else {
                    frame.setLocation(location);
                }
                frame.setVisible(true);
                frame.setAlwaysOnTop(true);
    
                new FadeTimer(frame, 1000, 0f, 1f, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Timer timer = new Timer(delay, new ActionListener() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                new FadeTimer(frame, 1000, 1f, 0f, new ActionListener() {
                                    @Override
                                    public void actionPerformed(ActionEvent e) {
                                        frame.dispose();
                                    }
                                }).start();
                            }
                        });
                        timer.setRepeats(false);
                        timer.start();
                    }
                }).start();
    
                return this;
            }
    
            public class FadeTimer extends Timer implements ActionListener {
    
                private final float startAt;
                private final float endAt;
                private final int duration;
                private long startTimer;
    
                private ActionListener endListener;
    
                private Window window;
    
                public FadeTimer(Window window, int duration, float startAt, float endAt, ActionListener endListener) {
                    super(5, null);
                    addActionListener(this);
                    this.duration = duration;
                    this.startAt = startAt;
                    this.endAt = endAt;
                    this.window = window;
    
                    this.endListener = endListener;
                }
    
                @Override
                public void start() {
                    startTime = System.currentTimeMillis();
                    super.start();
                }
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    long now = System.currentTimeMillis();
                    long lapsed = now - startTime;
                    float opacity = startAt;
                    if (lapsed >= duration) {
                        opacity = endAt;
                        ((Timer) e.getSource()).stop();
                        if (endListener != null) {
                            endListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "stopped"));
                        }
                    } else {
                        float progress = (float) lapsed / (float) duration;
                        float distance = endAt - startAt;
                        opacity = (float) (distance * progress);
                        opacity += startAt;
                    }
                    window.setOpacity(opacity);
                }
    
            }
    
            public class BackgroundPane extends JPanel {
    
                private MessagePane messagePane;
    
                public BackgroundPane() {
                    setBorder(new EmptyBorder(40, 40, 40, 40));
                    messagePane = new MessagePane();
                    setLayout(new BorderLayout());
                    add(messagePane);
                    setOpaque(false);
                }
    
                public void setMessage(String msg) {
                    messagePane.setMessage(msg);
                }
    
                @Override
                protected void paintComponent(Graphics g) {
                    super.paintComponent(g);
                    Graphics2D g2d = (Graphics2D) g.create();
                    g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
                    g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
                    g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                    g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                    g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                    LinearGradientPaint glp = new LinearGradientPaint(
                                    new Point(0, 0),
                                    new Point(0, getHeight()),
                                    new float[]{0f, 1f},
                                    new Color[]{Color.GRAY, Color.BLACK});
                    RoundRectangle2D frame = new RoundRectangle2D.Float(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
                    g2d.setPaint(glp);
                    g2d.fill(frame);
                    g2d.setColor(Color.WHITE);
                    g2d.draw(frame);
                }
    
            }
    
            public class MessagePane extends JPanel {
    
                private JLabel label;
    
                public MessagePane() {
                    setOpaque(false);
                    label = new JLabel();
                    label.setForeground(Color.WHITE);
                    setLayout(new GridBagLayout());
                    add(label);
                }
    
                public void setMessage(String msg) {
                    label.setText(msg);
                }
    
            }
    
        }
    
    }
    

    现在,您可能还可以通过更改框架的最大不透明度级别来做到这一点,但是,如果您更改 BackgroundPanepaintComponent

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.75f);
        g2d.setComposite(alpha);
    

    您还可以影响弹出消息的过度不透明度。此方法只会影响背景,不会影响消息文本...

    【讨论】:

    • 我下载了代码并试用了它,出于我的目的,我认为它可以工作,但是有一个问题。如果我尝试抽象部分代码以便我可以为文本气泡指定引号和位置,我会收到一个编译器错误,指出因为它使用内部类,所以我传入的参数必须声明为 final。现在错误本身很明显,但是我想知道在给定此设置的情况下,您将如何添加指定报价和位置的功能。我可以尝试完全重新整理这段代码,但我想知道是否有办法保留这种架构
    • 正如我所提到的,它实际上会创建一个带有一个或多个静态方法的工厂类。这些将创建窗口并应用消息,就像现在的构造函数一样。您可以通过将 setLocationRelativeTo 方法与窗口上的 setLocation 相联系来指定窗口的位置,其余的应该已经设置好...或多或少;)
    • 感谢您的建议,效果很好!我正在尝试让 alpha 合成工作,但我想我迷路了。我假设您尝试在绘制组件方法的背景窗格中应用 alpha 合成,从而影响 g2d,但无论我做什么都对透明度没有任何影响。我也尝试对 glp 做同样的事情,但我不认为 alpha 复合在那里适用。如果您对此有任何建议,我很想知道(既然您已经开始工作了。当然,请随意给我一个提示,那样会更有趣)
    • 嗯,有两种方法可以做到。一种是在绘制任何东西之前将其应用到背景上。另一种是设置窗口不透明度;)
    • @user3670987 添加了其他示例
    猜你喜欢
    • 2017-01-17
    • 2019-02-19
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    相关资源
    最近更新 更多