创建并使用一个不可见、最顶层、不可交互的窗口来进行“屏幕绘制”。

huangapple go评论89阅读模式
英文:

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

问题

我一直在努力让这个工作。我找到的唯一解决方案(虽然可行但糟糕)是在每一帧重新创建一个新窗口,这对性能和其他明显的原因都很糟糕。这是我拥有的代码,但我不确定如何在每一帧重绘,或者是否有其他方法可以实现相同的功能,我应该如何做?

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;
                    //绘制内容在这里(但似乎只绘制一帧)
                    //对于一些重绘似乎不起作用
        }
        
        @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'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.

答案1

得分: 0

你的问题相当基本 - 你需要一种定期更新用户界面的方法。

你的问题进一步复杂化了,因为Swing是单线程且不是线程安全的。幸运的是,Swing团队预见到了这个问题,并提供了Swing Timer 类。

首先查看Swing中的并发性如何使用Swing计时器以获取更多详细信息。

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();
        }
    }
}
英文:

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.

创建并使用一个不可见、最顶层、不可交互的窗口来进行“屏幕绘制”。

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 &lt;= 0.0) {
yDelta = 0;
direction = Direction.DOWN;
}
if (yPos &lt; 0) {
yPos = 0;
direction = Direction.DOWN;
}
} else if (direction == Direction.DOWN) {
yPos += yDelta;
yDelta += 0.01;
if (yDelta &gt; 2.0) {
yDelta = 2.0;
}
if (yPos + ball.getBounds().height &gt; 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();
}
}
}

答案2

得分: 0

MadProgrammer的解决方案大部分是正确的。但我进一步改进了一些东西。

我将JFrame更改为了JWindow,并删除了调用setUndecorated的部分。这保持了功能性并从任务栏中移除了窗口。

接下来,可以选择使用JNA允许所有输入通过窗口的组件。我的最终解决方案如下,感谢所有的帮助:

import java.awt.Color;
import java.awt.Component;
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.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import com.sun.jna.*;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinUser;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JWindow window = new JWindow();
                window.setBackground(new Color(0, true));
                window.add(new TestPane());
                window.pack();
                window.setLocationRelativeTo(null);
                window.setVisible(true);
                window.setAlwaysOnTop(true);
                setTransparent(window);
            }
        });
    }

    public static 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();
        }
    }
    
    private static void setTransparent(Component w) {
        WinDef.HWND hwnd = getHWnd(w);
        int wl = User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_EXSTYLE);
        wl = wl | WinUser.WS_EX_LAYERED | WinUser.WS_EX_TRANSPARENT;
        User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_EXSTYLE, wl);
    }

    /**
     * Get the window handle from the OS
     */
    private static HWND getHWnd(Component w) {
        HWND hwnd = new HWND();
        hwnd.setPointer(Native.getComponentPointer(w));
        return hwnd;
    }
}
英文:

MadProgrammer's solution was mostly correct. But I went a step further to address a few things.

I changed the JFrame to a JWindow and removed the call to setUndecorated. This kept functionality and removed the window from the taskbar.

Next and optionally, I used JNA to allow all inputs to pass through the window's components. My final solution is as follows, thanks for all of the help:

import java.awt.Color;
import java.awt.Component;
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.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import com.sun.jna.*;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinUser;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JWindow window = new JWindow();
window.setBackground(new Color(0, true));
window.add(new TestPane());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
window.setAlwaysOnTop(true);
setTransparent(window);
}
});
}
public static 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 &lt;= 0.0) {
yDelta = 0;
direction = Direction.DOWN;
}
if (yPos &lt; 0) {
yPos = 0;
direction = Direction.DOWN;
}
} else if (direction == Direction.DOWN) {
yPos += yDelta;
yDelta += 0.01;
if (yDelta &gt; 2.0) {
yDelta = 2.0;
}
if (yPos + ball.getBounds().height &gt; 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();
}
}
private static void setTransparent(Component w) {
WinDef.HWND hwnd = getHWnd(w);
int wl = User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_EXSTYLE);
wl = wl | WinUser.WS_EX_LAYERED | WinUser.WS_EX_TRANSPARENT;
User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_EXSTYLE, wl);
}
/**
* Get the window handle from the OS
*/
private static HWND getHWnd(Component w) {
HWND hwnd = new HWND();
hwnd.setPointer(Native.getComponentPointer(w));
return hwnd;
}
}

huangapple
  • 本文由 发表于 2023年7月11日 04:08:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76657011.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定