在另一个JFrame上显示JPanel

4

当前状态: 我有一个包含复杂组件(由我自己编写的3D画布)的JPanel对象。

问题: 现在有两个屏幕设备。我想使用一个用于控制,另一个仅用于显示,就像PowerPoint一样。如何高效地在另一个屏幕上显示这个JPanel(静态视图已足够,但我希望它能反映在控制屏幕上的更改呢?

我尝试过的: 我尝试每200毫秒将静态图片绘制到另一个JPanel中。

            Graphics2D g2 = (Graphics2D) contentPanel.getGraphics();
            g2.translate(x, y);
            g2.scale(scale, scale); 
            displayPanel.paintAll(g2);

注:contentPanel位于显示屏的JFrame中,displayerPanel是我想要复制的面板

但我遇到了一个问题,显示屏闪烁得如此严重,以至于我无法接受这个问题...这是我的CPU或显卡的问题吗?还是有什么有效的方法可以使用?请帮忙,非常感谢!

这是MCVE(对不起,这是我第一次在stackoverflow上提问,但我被你们的帮助所感动!再次感谢!)

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.Timer;

public class DisplayWindow {

private static JFrame frame = new JFrame();
private static JPanel contentPanel = new JPanel();
private static JPanel displayPanel = null;
private static Timer timerDisplay = null;


static {
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(50, 50, 1366, 768);
    frame.getContentPane().add(contentPanel);
    frame.setVisible(true);
    if (timerDisplay == null) {
        timerDisplay = new java.util.Timer();
        timerDisplay.schedule(new DisplayAtFixedRate(), 1000, 250);
    }
}

public static void display(JPanel panel) {
    displayPanel = panel;
}

private static class DisplayAtFixedRate extends TimerTask {
    @Override
    public void run() {
        if (displayPanel != null && displayPanel.getWidth() != 0) {
            double windowWidth = frame.getWidth();
            double windowHeight = frame.getHeight();
            double panelWidth = displayPanel.getWidth();
            double panelHeight = displayPanel.getHeight();
            double scale = Math.min(windowWidth / panelWidth, windowHeight / panelHeight);
            int x = (int) (windowWidth - panelWidth * scale) / 2;
            int y = (int) (windowHeight - panelHeight * scale) / 2;

            Graphics2D g2 = (Graphics2D) contentPanel.getGraphics();
            g2.translate(x, y);
            g2.scale(scale, scale);
            displayPanel.paintAll(g2);
        }
    }
}

public static void main(String[] args) {
    JFrame controlFrame = new JFrame();
    controlFrame.setBounds(50, 50, 1366, 768);
    JPanel controlPanel = new JPanel();
    controlFrame.getContentPane().add(controlPanel, BorderLayout.CENTER);
    controlPanel.add(new JLabel("Hello Stackoverflow!"));
    controlFrame.setVisible(true);
    DisplayWindow.display(controlPanel);
}
}

每次计时器触发时,您是否需要进行翻译和缩放?也许在面板上调用repaint会减少闪烁,因为它不涉及组件的调整大小等操作。 - Ashwinee K Jha
2
  1. 为了更快地获得更好的帮助,请发布一个[MCVE]或简短,自包含,正确的示例
  2. 参见使用多个JFrames,好/坏实践?
- Andrew Thompson
1
我尝试每200毫秒将静态图片绘制到另一个JPanel上。- 你是否尝试使用SwingTimer?因为TimerTask的信号不会通知EDT。 - mKorbel
我看到了你的编辑,但是MCVE在哪里? - Andrew Thompson
如果我只调用repaint(),displayPanel的最新更改将无法反映在contentPanel中。 - Bode
显示剩余4条评论
2个回答

2

这里有一个例子:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;

public class PaintImage {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                startUI();
            }
        });
    }

    private static void startUI() {
        final JFrame mainFrm = new JFrame("Main");
        final JFrame paintFrame = new JFrame("Copy");
        mainFrm.add(new JScrollPane(new JTree()), BorderLayout.WEST);
        mainFrm.add(new JScrollPane(new JTextArea("Write your text here...")), BorderLayout.CENTER);
        mainFrm.pack();
        mainFrm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        mainFrm.setLocationRelativeTo(null);

        final JLabel label = new JLabel("");
        paintFrame.add(label);
        paintFrame.setSize(mainFrm.getSize());
        paintFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        mainFrm.setVisible(true);
        paintFrame.setVisible(true);

        final Timer t = new Timer(200, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                final Image img = getScreenShot(mainFrm.getContentPane());
                label.setIcon(new ImageIcon(img));
            }
        });
        t.start();
    }

    public static BufferedImage getScreenShot(Component component) {

        final BufferedImage image = new BufferedImage(
                component.getWidth(),
                component.getHeight(),
                BufferedImage.TYPE_INT_RGB
                );
        // call the Component's paint method, using
        // the Graphics object of the image.
        component.paint( image.getGraphics() ); // alternately use .printAll(..)
        return image;
    }
}

Origin of method getScreenShot is here


太棒了!使用您的示例后,它的表现好多了!我认为SwingTimer是关键问题。非常感谢! - Bode

0

可以通过将 displayPanel 传递给自身绘制来完成绘画。

class DuplicatePanel extends JPanel {
    private final JPanel displayPanel;

    DuplicatePanel(JPanel displayPanel) {
        this.displayPanel = displayPanel;
    }

    @Override
    public void paintComponent(Graphics g) {
        displayPanel.paintComponent(g);
    }
}

要触发重绘,例如repaint(50L),可以使用SwingTimer或自己手动重绘。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接