【问题标题】:Painting issue in custom layered container?自定义分层容器中的绘画问题?
【发布时间】:2014-03-31 16:47:19
【问题描述】:

所以我正在这里做一个需要自定义JLayeredPane 的项目——比如类。 它有两个成员“ground”和“foreground”,分别是 JPanel 和一个 background (Image) 成员。

它应该显示的方式是应该绘制背景图像,然后在其顶部绘制地面的所有组件,然后在顶点处绘制前景的组件。所以前景覆盖了地面,地面覆盖了背景。仅应在地面和前景中没有Component 的地方或JPanels 中有透明度的地方显示背景。

它的绘画功能是这样的:

@Override
public void paint(Graphics g){
   g.drawImage(background, 0, 0, null);
   ground.paint(g.create());
   foreground.paint(g.create());
   g.dispose();
}

但不会发生这样的事情。只是背景图像被绘制,没有其他显示。

我已经使用System.out.println() 函数来检查地面和前景是否确实包含组件,并且它们确实存在。但他们只是不显示。

有人可以帮我吗?

【问题讨论】:

  • groundforeground 到底是什么?我感觉你的做法是错误的。也许在这方面和实际要求上更详细一点。
  • foreground 和 ground 是属于自定义 JLayeredPane 的 Jpanel 对象。我需要的与谷歌地图的外观没有什么不同。它必须是标志作为背景,地图在地面,HUD 在前景。由于地图的未加载部分将允许看到徽标。

标签: java paint layer pane


【解决方案1】:

最重要的问题是您没有调用 super.paint,这会阻止之前绘制到 Graphics 上下文的内容被清除或绘制任何子组件。

要绘制背景,您应该使用paintComponent,用于绘制组件的背景。

如果需要在子组件下方绘制,但在背景上方,仍应使用paintComponent,但先绘制背景,然后再绘制下一层。组件将在paintComponent 之后绘制。

在组件上绘制实际上更复杂

仔细看看Custom PaintingPainting in Swing and AWT

根据代码 sn-ps 更新

在从Container 延伸的Screen 中,你正在做...

@Override
public void paint(Graphics g) {
    super.paint(g);
    GraphicsUtilities.drawPictureTiled(background, g);
    paintComponents(g);
    g.dispose();
}
  • 不要打电话给paintComponentssuper.paint已经这样做了。
  • 请勿在您未创建的 Graphics 上下文中调用 dispose
  • 根据我所拥有的示例代码的其余部分,您应该从JPanel 扩展并覆盖paintComponent。这将允许您放置在组件层下

因为GroundPanelForeGroundPanel 都是JPanels,所以无需自己绘制它们。实际上,您可以简单地使用 OverlayLayout 甚至 GridBagLayout 并将它们直接添加到 NestedScreen 本身就是一个容器...

所以,我剥离了您的示例代码,以便我可以使用缺少的代码作为示例。我更花哨了,简单地做了一个JPanel 来充当暂停屏幕

这一切都是通过使用 GridBagLayout 将组件相互叠加来完成的

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test1001 {

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

    public Test1001() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                try {
                    NestedScreen screen = new NestedScreen();
                    screen.setBackgroundLayer(ImageIO.read(getClass().getResource("/Sky.png")));

                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(screen);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

    public interface GraphicsEngineComponents {

    }

    public class NestedScreen extends Screen implements GraphicsEngineComponents {

        GroundPanel ground;
        ForeGroundPanel foreground;
        private PausePane pausePane;

        public NestedScreen() {

            ground = new GroundPanel();
            foreground = new ForeGroundPanel();
            pausePane = new PausePane();

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;

            add(pausePane, gbc);
            add(foreground, gbc);
            add(ground, gbc);

            MouseAdapter handler = new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    pausePane.setVisible(!pausePane.isVisible());
                }
            };

            addMouseListener(handler);
            foreground.addMouseListener(handler);
            ground.addMouseListener(handler);

        }

        public GroundPanel getGroundLayer() {
            return ground;
        }

        public ForeGroundPanel getForegroundLayer() {
            return foreground;
        }

        public void setBackgroundLayer(BufferedImage background) {
            super.setBackgroundLayer(background);
        }

        public class GroundPanel extends JPanel {

            public GroundPanel() {
                setOpaque(false);
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(Color.GREEN);
                g.fillRect(0, getHeight() - 200, getWidth(), 200);
            }

        }

        public class PausePane extends JPanel {

            private JLabel label;

            public PausePane() {
                setVisible(false);
                setOpaque(false);
                setBackground(new Color(0, 0, 0, 128));
                setLayout(new GridBagLayout());

                label = new JLabel("Paused");
                label.setHorizontalAlignment(JLabel.CENTER);
                label.setVerticalAlignment(JLabel.CENTER);
                Font font = label.getFont();
                font = font.deriveFont(Font.BOLD, 48f);
                label.setFont(font);
                label.setForeground(Color.WHITE);
                add(label);
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(getBackground());
                g.fillRect(0, 0, getWidth(), getHeight());
            }

        }

        public class ForeGroundPanel extends JPanel {

            private BufferedImage pony;

            public ForeGroundPanel() {
                setOpaque(false);
                try {
                    pony = ImageIO.read(getClass().getResource("/Pony.png"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (pony != null) {
                    int x = (getWidth() - pony.getWidth()) / 2;
                    int y = getHeight() - 200 - (pony.getHeight() / 2);
                    g.drawImage(pony, x, y, this);
                }
            }

        }
    }

    public class Screen extends JPanel implements GraphicsEngineComponents {

        private BufferedImage background;

        public Screen() {
        }

        @Override
        public String toString() {
            return "Screen{" + "background=" + background + '}';
        }

        public BufferedImage getBackgroundPicture() {
            return background;
        }

        @Override
        public Dimension getPreferredSize() {
            return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
        }

        protected void setBackgroundLayer(BufferedImage background) {
            if (background != null && background.getHeight() != 0 && background.getWidth() != 0) {
                this.background = background;
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (background != null) {
                g.drawImage(background, 0, 0, this);
            }
        }
    }

}

查看 Painting in AWT and SwingPerforming Custom Painting 以了解在 Swing 中绘画的工作原理。

一个基本的想法是避免使用所有这些复合或嵌套组件,而是创建一个引擎,可以将图层直接绘制到Graphics 上下文,甚至可以绘制到您可以绘制到的BufferedImage单个组件...

【讨论】:

  • 您能详细说明一下吗?我实际上不想清除以前的东西,因为它包括背景。好吧,我试着打电话给super.paint()。还是不行。现在我首先绘制背景,然后调用 super,然后在地面和前景上调用 paintComponent()。需要帮助!
  • Graphics 是共享资源,这意味着在您的组件之前绘制的内容在您收到它时仍然“存在”。你必须先清除它,这是油漆链的作用
  • 那么为什么我需要清除它,而所有这些都可以被覆盖?那不是更节省资源吗?或者不会?要求答复。谢谢。
  • 因为它符合API的期望
  • 我添加了 getPreferedSize() 方法来返回完整的正大小,并覆盖了 isVisible() 以始终返回 true。它仍然不起作用。需要帮助!
【解决方案2】:

有几种方法可以做到这一点。我只介绍一种方式。

  • 创建一个背景面板,您可以在其中绘制背景图像(在下面的示例中,它是BackgroundPanel,图像只是作为背景)。将该面板设置为框架的内容窗格。

  • 创建另一个地面面板,您还可以在其中绘制一些东西(在下面的示例中,如果它是 GroundPanel,只绘制了兔子的图像。

  • 创建前景面板并将其添加到地面面板。您可以将前景组件添加到其中。 (在下面的例子中,前景图是草山,我还给它加了一个按钮

  • 所有面板的 opaque 属性都应设置为 false,以允许其后面的面板以任何透明度显示。

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class ThreeTier {

    public static final int DIM_WIDTH = 600;
    public static final int DIM_HEIGHT = 400;

    private BufferedImage backgroundImage;
    private BufferedImage groundImage;
    private BufferedImage foregroundImage;

    public ThreeTier() {
        initImages();
        JFrame frame = new JFrame();
        frame.setContentPane(new BackgroundPanel());
        frame.add(new GroundPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void initImages() {
        try {
            backgroundImage = ImageIO.read(getClass().getResource("/resources/background.png"));
            foregroundImage = ImageIO.read(getClass().getResource("/resources/foreground.png"));
            groundImage = ImageIO.read(getClass().getResource("/resources/bugsBunny.png"));
        } catch (IOException ex) {
            Logger.getLogger(ThreeTier.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    class BackgroundPanel extends JPanel {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(backgroundImage, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
        }
    }

    class GroundPanel extends JPanel {

        private static final String RIGHT_ACTION = "rightAction";
        private int imageX = 50;
        private int imageY = 140;

        public GroundPanel() {
            setOpaque(false);
            add(new ForegroundPanel());

            InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke("RIGHT"), RIGHT_ACTION);
            getActionMap().put(RIGHT_ACTION, new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    if (imageX >= DIM_WIDTH) {
                        imageX = 0 - groundImage.getWidth();
                        repaint();
                    } else {
                        imageX += 10;
                        repaint();
                    }

                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(groundImage, imageX, imageY, groundImage.getWidth(), groundImage.getHeight(), this);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
        }
    }

    class ForegroundPanel extends JPanel {

        public ForegroundPanel() {
            setOpaque(false);
            setLayout(new FlowLayout(FlowLayout.TRAILING, 10, 0));
            JButton button = new JButton("I'm in the Foreground!");
            add(button);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(foregroundImage, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
        }
    }

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

更新

当然,您总是可以使用JLayeredPane。这就是它的用途。看这个例子

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class LayeredPaneDemo {

    public static final int DIM_WIDTH = 600;
    public static final int DIM_HEIGHT = 400;

    public LayeredPaneDemo() {
        ContainerPanel container = new ContainerPanel();
        JLabel title = new JLabel("Lame Google Map");
        title.setFont(new Font("verdana", Font.BOLD, 36));
        title.setHorizontalAlignment(JLabel.CENTER);
        JPanel panel = new JPanel(new GridBagLayout());
        panel.add(container);
        JFrame frame = new JFrame();
        frame.add(panel);
        frame.add(title, BorderLayout.NORTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

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

    public class ContainerPanel extends JPanel {

        public ContainerPanel() {
            JLayeredPane layeredPane = new JLayeredPane();
            layeredPane.setPreferredSize(new Dimension(DIM_WIDTH, DIM_HEIGHT));
            BackgroundPanel bg = new BackgroundPanel();
            GroundPanel gg = new GroundPanel();
            ForegroundPanel fg = new ForegroundPanel();

            bg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
            layeredPane.add(bg, new Integer(1));
            gg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
            layeredPane.add(gg, new Integer(2));
            fg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
            layeredPane.add(fg, new Integer(3));
            setLayout(new GridBagLayout());
            add(layeredPane);

            setBorder(new LineBorder(Color.BLUE, 10));
        }
    }

    public class ForegroundPanel extends JPanel {

        public ForegroundPanel() {
            JPanel buttonPanel = new JPanel(new GridLayout(3, 3));
            buttonPanel.setOpaque(false);
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("UP"));
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("Left"));
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("Right"));
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("Down"));
            buttonPanel.add(new JLabel());

            FlowLayout flow = (FlowLayout) getLayout();
            flow.setAlignment(FlowLayout.TRAILING);
            flow.setHgap(0);
            flow.setVgap(0);
            add(buttonPanel);
            setOpaque(false);

        }
    }

    public class GroundPanel extends JPanel {

        Image image = null;

        public GroundPanel() {

            try {
                image = ImageIO.read(getClass().getResource("/resources/california.png"));
            } catch (IOException ex) {
                Logger.getLogger(LayeredPaneDemo.class.getName()).log(Level.SEVERE, null, ex);
            }

            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(image, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
        }
    }

    public class BackgroundPanel extends JPanel {

        public BackgroundPanel() {
            setLayout(new GridBagLayout());
            setBackground(Color.WHITE);
            try {
                Image img = ImageIO.read(getClass().getResource("/resources/google.jpg"));
                add(new JLabel(new ImageIcon(img)));
            } catch (IOException ex) {
                Logger.getLogger(LayeredPaneDemo.class.getName()).log(Level.SEVERE, null, ex);
            }


        }
    }
}

【讨论】:

  • 这会解决我的问题,但你确定它有效吗?但无论如何,我会尝试并让你知道几个小时。百万谢谢。
  • 嗯,它部分解决了我的问题。但问题是我也需要将组件添加到地面层。因此,如果您在GroundPanel 中添加组件,您的系统将无法工作,因为前景将与它的组件一起被绘制,而不是在它之后。感谢您的努力。
  • 唯一只是图像的层是背景层,但我已经弄清楚了。请帮我处理前台的事情!感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 2016-02-05
  • 1970-01-01
  • 2019-10-30
  • 2020-06-06
  • 2020-04-04
  • 1970-01-01
相关资源
最近更新 更多