How to pop up a text box(or tooltip) via script in

2020-05-05 18:09发布

I have a very specific question: I want to be able to, via a method call, popup a tooltip with text(it could say anything) in a given location on the screen for a certain amount of time on method call(say the logic is in a talk method) and fades away. How could I go about that? Is there a way to do it via JTooltip? Or would I have to dive into JNA to get what I want?

I should mention I want the tooltip to popup with text in a given location without the cue of a mouse over, like a popup.

Also, in case a tooltip is not the right way to go about what I want (which I hope I made clear), is there a more effective alternative?

1条回答
Root(大扎)
2楼-- · 2020-05-05 18:17

There are few ways this might be achieved. One possible way is through the use of a transparent JWindow and a Swing Timer

Basically, what this does is creates a JWindow, set's it's background color to fully transparent, making a transparent window. It then uses simple BackgroundPane (to render a nice background) and MessagePane to hold the actual message. You can do this in one panel, but I like the flexibility this affords me.

Popup

Now, personally, I would create a simpler API which could build the popup window and create a Timer with a variable delay, but you get the idea

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);
        }

    }

}

You could play with a AlphaComposite on the background panel to create a semi transparent background

Popup window using a 50% AlphaComposite

Popup

Updated

You could use a factory or builder pattern to provide a simple API, for example...

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

The builder would collect the properties you want to specify, provide defaults where you didn't set them and then would show the final popup.

The basic idea is when you call show, it would collect the properties and build the window similar to how the constructor works right now...

Updated with a fading popup window

This is (a somewhat over the top) example of how you might be able to produce a fading in/out effect. The example guarantees that the message will be on the screen (full) for at specified delay period

Popup

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);
            }

        }

    }

}

Now, you probably also do this by changing the maximum opacity level of the frame, but, if you change the paintComponent of the BackgroundPane

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

you can also effect the over opacity of the popup message. This method will only effect the background, not the message text...

查看更多
登录 后发表回答