简体   繁体   English

如何在 Swing 中重叠面板?

[英]How to Overlap Panels in Swing?

I am attempting to have multiple JPanel s that can "overlap", also allowing me to perform custom painting.我试图让多个JPanel可以“重叠”,也允许我执行自定义绘画。

For this I am using a MainPanel , which extends JLayeredPane , and from what I can see, I have set bounds and index correctly.为此,我使用了MainPanel ,它扩展了JLayeredPane ,据我所知,我已经正确设置了边界和索引。

The expected result, would be two rectangles painting at the same time to the screen.预期的结果是两个矩形同时绘制到屏幕上。

The result I get, is flickering on one of the two OverlappingPanel s, which I assume is from the RepaintManager fighting on which panel to draw ( Found this here ).我得到的结果是在两个OverlappingPanel之一上闪烁,我认为这是来自RepaintManager在哪个面板上绘制(在此处找到)。

My question is, How can I properly overlap panels and retain painting capabilties, using Swing?我的问题是,如何使用 Swing 正确重叠面板并保留绘画能力?

EDIT:编辑:

Code in question:有问题的代码:

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

So yes, a JLayeredPane would allow easy overlap of Swing components such as JPanels, and there are also layouts others have created that allow this, one called "overlay layout", but that's not what you want to for your currently stated problem.所以是的,JLayeredPane 将允许 Swing 组件(例如 JPanel)轻松重叠,并且还有其他人创建的布局允许这样做,称为“叠加布局”,但这不是您当前所说的问题想要的。

Yours is an XY Problem type question where you ask "how do I solve X problem" when the best solution is not to solve it in this way, but rather to do Y, something completely different.你的问题是一个XY 问题类型的问题,当最好的解决方案不是以这种方式解决它,而是做 Y,完全不同的事情时,你会问“我如何解决 X 问题”。 Here, to paint multiple different images, your best solution is not to create and overlap heavier-weight Swing components such as JPanels, but rather to draw in one single JPanel and overlap sprite images.在这里,要绘制多个不同的图像,最好的解决方案不是创建和重叠较重的 Swing 组件(例如 JPanel),而是绘制单个 JPanel 并重叠精灵图像。 Otherwise you're just making things unnecessarily harder for yourself and your code than is needed.否则,您只会使您自己和您的代码变得不必要地困难,而不是需要。

For example:例如:

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

And yes, as suggested in comments, override paintComponent , not paint .是的,正如评论中所建议的那样,覆盖paintComponent而不是paint This reduces the risk of unwanted side effects that might come from painting child components or borders, and also allows for automatic double-buffering for when you do animation.这降低了可能来自绘制子组件或边框的不良副作用的风险,并且还允许在您执行 animation 时进行自动双缓冲。

Also, a while (true) loop is not a healthy construct within an event-driven GUI program, not as you've written it.此外,在事件驱动的 GUI 程序中, while (true)循环不是一个健康的结构,而不是像您编写的那样。 If you need repeated actions in a Swing program (which you don't in your example, not yet), use a Swing Timer instead.如果您需要在 Swing 程序中重复操作(在您的示例中还没有),请改用Swing 计时器

So doing this this way gives you good flexibility.所以这样做会给你很好的灵活性。 For instance, if you wanted to modify the above program to allow addition of shapes on mouse click, it would be easy to do so:例如,如果您想修改上述程序以允许在鼠标单击时添加形状,这样做很容易:

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

The last two parameteres in setBounds(int x,int y, int width, int height) are the width and height of your panel. setBounds(int x,int y, int width, int height)中的最后两个参数是面板的宽度和高度。 In your case, these are the dimensions of your rectangle, thus you should set them to 200 , as below:在您的情况下,这些是矩形的尺寸,因此您应该将它们设置为200 ,如下所示:

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

Also, remove setPreferredSize(new Dimension(1920,720));另外,删除setPreferredSize(new Dimension(1920,720)); in the OverlappingPanel1 and OverlappingPanel2 classes, as they are not needed.OverlappingPanel1OverlappingPanel2类中,因为它们不是必需的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM