Java:为什么背景图像涂在按钮上?

时间:2014-05-26 23:19:21

标签: java swing jpanel jbutton paintcomponent

是否可以在同一JPanel中的按钮后面绘制更新图像?我正在尝试使用菜单屏幕,但是当我尝试放入一个绘制循环来更新背景图像时,按钮会消失。注意:这些是自定义按钮,它们是不可见的,除了我使用JButton.setIcon放入它们的自定义图像,如果它在渲染中有任何不同。

public class MenuPanel extends JPanel{
    BufferedImage image;
    private int height =Toolkit.getDefaultToolkit().getScreenSize().height-37;
    private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
    private JButton optionsButton = new JButton("Options");
    private JButton startButton = new JButton ("Start");

    public MenuPanel() {
        setLayout(null);
        setSize(width, height);

        try {image = ImageIO.read(new File("stuyrim.png"));}
        catch (Exception e) {Utilities.showErrorMessage(this, e);}

        optionsButton.addActionListener(e -> {
        //      optionsButton.getParent().getParent();
            });
        optionsButton.setOpaque(false);
        optionsButton.setBorderPainted(false);
        optionsButton.setContentAreaFilled(false);
        optionsButton.setSize(width/10,height/20);
        optionsButton.setVerticalTextPosition(SwingConstants.CENTER);
        optionsButton.setHorizontalTextPosition(SwingConstants.CENTER);
        optionsButton.setFont(new Font("TimesRoman", Font.PLAIN, 20));
        optionsButton.setLocation((width/3)-(width/20), (height/7*6)-(height/40));
        Image img = new ImageIcon("GUI Images/Button.png").getImage().getScaledInstance
            (optionsButton.getWidth(),optionsButton.getHeight(),java.awt.Image.SCALE_SMOOTH);
        optionsButton.setIcon(new ImageIcon(img));
        optionsButton.setForeground(Color.white);


        startButton.addActionListener(e -> {
        startButton.getParent().getParent().add(new Screen());
        startButton.getParent().getParent().add(new GamePanel());
        Graphics g = getGraphics();
        g.clearRect(0,0,width,height);
        startButton.getParent().getParent().remove(this);
        g.dispose();
            });
        startButton.setOpaque(false);
        startButton.setBorderPainted(false);
        startButton.setContentAreaFilled(false);
        startButton.setSize(width/10,height/20);
        startButton.setVerticalTextPosition(SwingConstants.CENTER);
        startButton.setHorizontalTextPosition(SwingConstants.CENTER);
        startButton.setFont(new Font("TimesRoman",Font.PLAIN, 20));
        startButton.setLocation((width/3*2)-(width/20), (height/7*6)-(height/40));
        Image img1 = new ImageIcon("GUI Images/Button.png").getImage().getScaledInstance
            (optionsButton.getWidth(),optionsButton.getHeight(),java.awt.Image.SCALE_SMOOTH);
        startButton.setIcon(new ImageIcon(img1));
        startButton.setForeground(Color.white);
        add(optionsButton);
        add(startButton);
        setVisible(true);
        revalidate();
        repaint();
    }

    public void paintComponent(Graphics g) {
        g.drawImage(image,0,0,width,height, null);
        g.dispose();
    }

}

3 个答案:

答案 0 :(得分:2)

有两个基本问题......

第一个,正如System.exit已经突出显示的那样,你应该在执行任何自定义绘画之前调用super.paintComponent

原因是传递给Graphics的{​​{1}}上下文是共享资源。在您的组件之前绘制的所有内容以及在其之后绘制的所有内容都将共享相同的paintComponent上下文。

Graphics的一项工作是为绘画准备paintComponent上下文(通常通过使用组件的背景颜色填充所需空间)。

第二个是使用GraphicsgetGraphics获取对用于绘制组件的最后getGraphics上下文的引用(如果组件尚未绘制,则可以为Graphics),如果您使用此方法,则在Swings外部绘制定义的油漆链,可以产生随机和不可预测的结果。

Swing使用被动渲染算法,即绘画以不规则的间隔发生,并且可能由于多种原因而发生,其中许多原因是您无法控制或未启动。

所有自定义绘画都应该在Swing定义的绘制链中完成,按照惯例,这通常通过覆盖null方法来完成。

请查看Performing Custom PaintingPainting in AWT and Swing了解详情

答案 1 :(得分:1)

答案是:是的。当然可以。但是代码中缺少一个关键的东西。你提到的那个“循环”。 Swing是单线程的,应该在EDT(事件调度线程)上更新。在您的情况下,您应该使用swing Timer组件来更新GUI,同时保持响应。这是一个简单的演示:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;


public class JPanelDemo {

    JFrame frame = new JFrame();
    Panel panel = new Panel();
    JButton button1 = new JButton("Button 1");
    JButton button2 = new JButton("Button 2");

    public JPanelDemo() {
        panel.add(button1);
        panel.add(button2);
        frame.add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //frame.pack();
        frame.setSize(640, 480);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(JPanelDemo::new);
    }

    class Panel extends JPanel {
        Timer timer;

        //Example images
        String[] images = {"C:/Users/yourname/Pictures/Koala.jpg",
                "C:/Users/yourname/Pictures/Flower.jpg"};

        BufferedImage image;
        int counter;

        public Panel() {
            // Use proper component to update your swing gui.
            // This is actually "The paint loop"
            timer = new Timer(0, ae -> {
                try {
                    image = ImageIO.read(new File(images[counter]));
                    repaint();
                    revalidate();
                    counter++;
                    if (counter == images.length) {
                        counter = 0;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            timer.setRepeats(true);
            timer.setDelay(5000);
            timer.start();

        }

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

    }
}

我使用swing Timer每隔5秒重复更新JPanel的背景,同时我的GUI仍然响应。

除此之外,我在你的代码中发现了一个更有问题的东西。您已为null设置JPanel布局。如果您不想遇到麻烦,请避免支付任何费用。请改用适当的布局管理器。

答案 2 :(得分:0)

尝试像这样呼叫super.paintComponent

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(image,0,0,width,height, null);
    g.dispose();
}