【问题标题】:JButton with image on JPanel with image not rendering properlyJButton 与 JPanel 上的图像与图像未正确呈现
【发布时间】:2012-12-12 02:29:12
【问题描述】:

好的,我又来了。

我有一个 JPanel,我已在其上覆盖 paintComponent() 并在自定义 bufferedImage 上绘制,以为我的 JPanel 提供背景图像。我也有一个 JButton,我做了同样的事情,以便创建一个具有自定义形状的按钮。问题是,虽然 JButton 似乎已正确添加到带有背景图像的 JPanel 中,但并未正确绘制。

我可以从按钮获得输入,就好像它确实存在一样,但它实际上并没有显示出来,而它在标准 JPanel 上显示。下面是一个例子。

提前感谢您的帮助。

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class Test {
    public static void main (String[] args){
        final JFrame frame = new JFrame();
        DrawPanel panel1 = new DrawPanel(createBufferedImage("background.png"));
        JPanel panel2 = new JPanel();

        DrawButton button1 = new DrawButton(createBufferedImage("button.png"));
        button1.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                JOptionPane.showMessageDialog(frame, "input detected, button1");
            }
        });

        DrawButton button2 = new DrawButton(createBufferedImage("button.png"));
        button2.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                JOptionPane.showMessageDialog(frame, "input detected, button2");
            }
        });

        panel1.add(button1);
        panel2.add(button2);
        frame.getContentPane().add(panel1, BorderLayout.NORTH);
        frame.getContentPane().add(panel2, BorderLayout.CENTER);
        frame.validate();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    protected static BufferedImage createBufferedImage(String path){
        File img = new File(path);
        BufferedImage bi = null;
        try{
            bi = ImageIO.read(img);
        }
        catch (IOException ioe){
            throw new RuntimeException();
        }
        BufferedImage newImage = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = newImage.createGraphics();
        g2d.drawImage(bi, 0, 0, null);
        g2d.dispose();  
        return newImage;
    }
}

class DrawPanel extends JPanel{

    private BufferedImage bg; 

    public DrawPanel(BufferedImage bg){
        this.bg = bg;
        new JPanel();
        setPreferredSize(new Dimension(bg.getWidth(), bg.getHeight()));
    }

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

class DrawButton extends JButton{

    private BufferedImage bi;

    public DrawButton(BufferedImage bi){
        setPreferredSize(new Dimension(bi.getWidth(), bi.getHeight()));
        this.bi = (BufferedImage) bi;
        setContentAreaFilled(false);
    }

    @Override
    public Dimension getPreferredSize(){
        return new Dimension(bi.getWidth(), bi.getHeight());
    }

    public BufferedImage getIconImage(){
        return bi;
    }

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

【问题讨论】:

  • DrawPanel构造函数中调用new JPanel();的目的是什么?为什么要创建一个从不需要、从不使用、从不分配给变量并在构造函数超出范围时简单地处理掉的对象?
  • @HovercraftFullOfEels 有……没有任何目的。我认为最初的 DrawPanel 只是一个 JPanel,而不是一个对象,我从来没有摆脱这个调用。哎呀。
  • class DrawButton extends JButton{ 不要那样做,只需为其添加一个图标,setContentAreaFilled(false) & setBorder(null)
  • @AndrewThompson 由于我使用按钮的方式,我需要将 DrawButton 创建为自己的类。有关更多信息,请参阅此问题:link。我认为没有必要具体解释为什么我这样做,因为为什么似乎与手头的问题无关,只是它是一个问题。我知道您建议的方法,并且我已经将其视为不适合我的用例而将其驳回。

标签: java swing jpanel jbutton paintcomponent


【解决方案1】:

永远不要对 JVM 给你的 Graphics 对象调用 dispose,只对你自己创建的 Graphics 对象调用。

这样就OK了:

    BufferedImage newImage = new BufferedImage(bi.getWidth(), bi.getHeight(), 
         BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = newImage.createGraphics();
    g2d.drawImage(bi, 0, 0, null);
    g2d.dispose();

但这不是:

protected void paintComponent(Graphics g){
    super.paintComponent(g);
    g.drawImage(bg, 0, 0, null);
    g.dispose();
}

JVM 可能(并且可能会)需要使用该对象进行进一步的绘制,包括绘制容器的子组件,如果您将其丢弃,则可以防止这种情况发生。

另一个建议:考虑覆盖 getPreferredSize,而不是 setPreferredSize。所以而不是这样:

setPreferredSize(new Dimension(bg.getWidth(), bg.getHeight()));

考虑:

@Override
public Dimension getPreferredSize() {
  if (bg == null) {
    return super.getPreferredSize();
  } else {
    return new Dimension(bg.getWidth(), bg.getHeight());
  }
}

这样,组件的首选大小被可靠地设置,组件的任何用户都无法更改它。

【讨论】:

  • 嘿!这解决了它!谢谢!我有一种感觉,这很简单,我做错了,感谢您指出。我会尽量记住这一点。
  • @user18:不客气。不过,为什么要使用new JPanel(); 行?
  • 刚刚看到上面的评论——new JPanel(); 只是个傻瓜。
  • 哦,嘿,这很有趣。很高兴知道,我会考虑的。他们说你每天都会学到一些新东西——看起来我已经准备好迎接这周剩下的时间了。再次感谢。
猜你喜欢
  • 2013-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-16
  • 2021-03-19
  • 2019-08-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多