【问题标题】:How to Overlap Panels in Swing?如何在 Swing 中重叠面板?
【发布时间】:2022-01-14 10:01:51
【问题描述】:

我正在尝试拥有多个可以“重叠”的JPanels,也允许我执行自定义绘画。

为此,我使用了MainPanel,它扩展了JLayeredPane,据我所知,我已经正确设置了边界和索引。

预期的结果是两个矩形同时绘制到屏幕上。

我得到的结果是在两个OverlappingPanels 之一上闪烁,我认为这是来自RepaintManager 在哪个面板上绘制 (Found this here)。

我的问题是,如何使用 Swing 正确重叠面板并保留绘画功能?

编辑:

有问题的代码:

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

public class Example extends JFrame {
    public static class MainPanel extends JLayeredPane implements Runnable {
        public OverlappingPanel1 overlappingPanel1;
        public OverlappingPanel2 overlappingPanel2;

        Thread mainThread;

        public void startMainThread() {
            mainThread = new Thread(this);
            mainThread.start();
        }

        public MainPanel() {
            this.setPreferredSize(new Dimension(1920,720));
            this.setBackground(Color.BLACK);
            this.setDoubleBuffered(true);

            overlappingPanel1 = new OverlappingPanel1();
            overlappingPanel2 = new OverlappingPanel2();

            overlappingPanel1.setBounds(0,0,1920,720);
            overlappingPanel2.setBounds(0,720/2,1920,720);

            add(overlappingPanel1,1);
            add(overlappingPanel2,2);
        }

        @Override
        public void run() {
            while(mainThread != null) {
                overlappingPanel1.repaint();
                overlappingPanel2.repaint();
            }
        }
    }

    public static class OverlappingPanel1 extends JPanel {
        public OverlappingPanel1() {
            setDoubleBuffered(true);
            setPreferredSize(new Dimension(1920,720));
        }

        public void paint(Graphics g) {
            super.paint(g);
            Graphics2D graphics2D = (Graphics2D) g;

            graphics2D.fillRect(0,0,200,200);
        }
    }

    public static class OverlappingPanel2 extends JPanel {
        public OverlappingPanel2() {
            setDoubleBuffered(true);
            setPreferredSize(new Dimension(1920,720));
        }

        public void paint(Graphics g) {
            super.paint(g);
            Graphics2D graphics2D = (Graphics2D) g;

            graphics2D.fillRect(0,80,200,200);
        }
    }

    public static void main(String[] args) {
        JFrame window = new JFrame();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setResizable(false);

        MainPanel mainPanel = new MainPanel();
        window.add(mainPanel);
        window.setBackground(Color.BLACK);
        window.pack();

        window.setLocationRelativeTo(null);
        window.setVisible(true);

        mainPanel.startMainThread();
    }
}

【问题讨论】:

  • 将相关代码放入问题中。
  • @JustanotherJavaprogrammer 提到我的问题的代码在消息末尾附加的超链接中
  • 这就是问题所在。在问题中包含代码。
  • 覆盖paintComponent而不是paint。绘画是旧的 AWT 方式。
  • Swing 默认为双缓冲,因此无需为每个面板显式设置。

标签: java swing graphics


【解决方案1】:

所以是的,JLayeredPane 将允许 Swing 组件(例如 JPanel)轻松重叠,并且还有其他人创建的布局允许这样做,称为“叠加布局”,但这不是您当前陈述的问题想要的.

你的问题是XY Problem 类型的问题,当最好的解决方案不是以这种方式解决它,而是做 Y,完全不同的事情时,你会问“我如何解决 X 问题”。在这里,要绘制多个不同的图像,您最好的解决方案不是创建和重叠较重的 Swing 组件(例如 JPanel),而是绘制单个 JPanel 并重叠 sprite 图像。否则,您只会使您自己和您的代码变得不必要地困难。

例如:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;

@SuppressWarnings("serial")
public class Example2 extends JPanel {
    private static final int MY_WIDTH = 1600;
    private static final int MY_HEIGHT = 720;
    List<Rectangle> rectangles = new ArrayList<>();

    public Example2() {
        setPreferredSize(new Dimension(MY_WIDTH, MY_HEIGHT));
        setBackground(Color.WHITE);
        
        rectangles.add(new Rectangle(0, 0, 200, 200));
        rectangles.add(new Rectangle(0, 80 + MY_HEIGHT / 2, 200, 200));
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        for (Rectangle rectangle : rectangles) {
            g2.fill(rectangle);
        }
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            Example2 example = new Example2();
            
            JFrame frame = new JFrame("GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(example);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

是的,正如 cmets 中所建议的那样,覆盖 paintComponent不是 paint。这降低了绘制子组件或边框可能产生的不良副作用的风险,并且还允许在您制作动画时自动进行双缓冲。

另外,while (true) 循环在事件驱动的 GUI 程序中不是一个健康的构造,而不是像您编写的那样。如果您需要在 Swing 程序中重复执行操作(在您的示例中不需要,目前还没有),请改用 Swing Timer

所以这样做会给你很好的灵活性。例如,如果您想修改上述程序以允许在鼠标单击时添加形状,这样做很容易:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;

@SuppressWarnings("serial")
public class Example3 extends JPanel {
    private static final int MY_WIDTH = 1600;
    private static final int MY_HEIGHT = 720;
    List<ColorShape> colorShapes = new ArrayList<>();

    public Example3() {
        setPreferredSize(new Dimension(MY_WIDTH, MY_HEIGHT));
        setBackground(Color.WHITE);
        
        addMouseListener(new MyMouse());
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        for (ColorShape colorShape : colorShapes) {
            colorShape.draw(g2);
        }
    }
    
    private class MyMouse extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            // create a random color
            float hue = (float) Math.random();
            float saturation = 1f;
            float brightness = (float) (0.5 * Math.random() + 0.5);
            Color color = Color.getHSBColor(hue, saturation, brightness);

            // create a new ColorShape, add to list, and repaint:
            colorShapes.add(new ColorShape(e.getPoint(), color));
            repaint();
        }
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            Example3 example = new Example3();
            
            JFrame frame = new JFrame("GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(example);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}
class ColorShape {
    private int width = 80;
    private Point location;
    private Color color;
    private Shape shape;

    public ColorShape(Point location, Color color) {
        this.location = location;
        this.color = color;
        int x = location.x - width / 2;
        int y = location.y - width / 2;
        shape = new Ellipse2D.Double(x, y, width, width);
    }
    
    public void draw(Graphics2D g2) {
        g2.setColor(color);
        g2.fill(shape);
    }
    
    public Point getLocation() {
        return location;
    }
}

【讨论】:

    【解决方案2】:

    setBounds(int x,int y, int width, int height) 中的最后两个参数是面板的宽度和高度。在您的情况下,这些是矩形的尺寸,因此您应该将它们设置为 200,如下所示:

    overlappingPanel1.setBounds(0,0,200,200);
    overlappingPanel2.setBounds(0,720/2,200,200);
    

    另外,删除 OverlappingPanel1OverlappingPanel2 类中的 setPreferredSize(new Dimension(1920,720));,因为它们不是必需的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-18
      • 1970-01-01
      • 1970-01-01
      • 2010-11-16
      • 2021-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多