javawindowawtrepaint

Creating and using an invisible, topmost, non-interactable window for "screen-drawing"


I've been struggling to get this to work. The only solution (but terrible solution) I found was re creating a new window every frame which is horrible for performance and other obvious reasons. Here is what I have but I'm unsure about how I can go about redrawing every frame. Or if there's another approach I can take to achieve the same functionality, how can I go about doing it?

public static Window w = null;

@SuppressWarnings("serial")
    private static void initializeWindow(final Dimension d) {
        int windowHeight = d.height - 1; 
        
        w = new Window(null) {
            
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                Graphics2D g2d = (Graphics2D) g;
                        //painting goes here (but it only seems to draw for one frame)
                        //for some repainting doesn't seem to work
            }
            
            @Override
            public void update(Graphics g) {
                paint(g);
                System.out.println(true);
            }
            
        };
        
        w.setAlwaysOnTop(true);
        w.setBounds(0, 0, d.width, windowHeight);
        w.setBackground(new Color(0, true));
        w.setVisible(true);
    }

I tried the code block attached and I expected to be able to update the window every frame with newly painted stuff.


Solution

  • You problem is pretty basic - you need some way to update the UI on a regular bases.

    Your problem is further complicated by the fact that Swing is single thread and NOT thread safe. Lucky for you, the Swing team fore saw this issue and provided the Swing Timer class.

    Start by seeing Concurrency in Swing and How to Use Swing Timers for more details.

    enter image description here

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.Shape;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.Ellipse2D;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    
    public class Test {
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame window = new JFrame();
                    window.setUndecorated(true);
                    window.setBackground(new Color(0, true));
                    window.add(new TestPane());
                    window.pack();
                    window.setLocationRelativeTo(null);
                    window.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
            enum Direction {
                UP, DOWN;
            }
    
            private Timer timer;
    
            private Direction direction = Direction.UP;
            private double yDelta = 2;
            private double yPos = 380;
    
            private Shape ball = new Ellipse2D.Double(0, 0, 20, 20);
    
            public TestPane() {
                setOpaque(false);
    
                timer = new Timer(5, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (direction == Direction.UP) {
                            yPos -= yDelta;
                            yDelta -= 0.006;
                            if (yDelta <= 0.0) {
                                yDelta = 0;
                                direction = Direction.DOWN;
                            }
                            if (yPos < 0) {
                                yPos = 0;
                                direction = Direction.DOWN;
                            }
                        } else if (direction == Direction.DOWN) {
                            yPos += yDelta;
                            yDelta += 0.01;
                            if (yDelta > 2.0) {
                                yDelta = 2.0;
                            }
                            if (yPos + ball.getBounds().height > getHeight()) {
                                yPos = getHeight() - ball.getBounds().height;
                                direction = Direction.UP;
                                yDelta = 2;
                            }
                        }
                        repaint();
                    }
                });
            }
    
            @Override
            public void addNotify() {
                super.addNotify();
                timer.start();
            }
    
            @Override
            public void removeNotify() {
                timer.stop();
                super.removeNotify();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 400);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                RenderingHints hints = new RenderingHints(
                        RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
                );
                g2d.setColor(Color.RED);
                g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
                g2d.translate(190, yPos);
                g2d.fill(ball);
                g2d.dispose();
            }
        }
    }