Java中JButton上的ImageIcon会复制图像

3

本文的目的是弄清楚为什么两个按钮都会复制两张图片,这非常奇怪,不应该发生这种情况。那就是主要目标。然后就是找到一个解决方案。谢谢!

这是它的外观图像

enter image description here

我已经制作出最小可复现示例(MRE)。

它在两个按钮上都输出了相同的两张图片,我不知道为什么。

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GameManager extends JFrame{

    private final   Map <String, String> images = new HashMap<>(2);

    GameManager()
    {
        images.put("Articuno", "https://i.ya-webdesign.com/images/articuno-transparent-pokemon-xy-17.gif");
        images.put("Rayquaza", "https://play.pokemonshowdown.com/sprites/ani-back-shiny/rayquaza.gif");

        JPanel pnlPokemonInParty = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        //Why does it put both images on both buttons? It actually sets the images on top of one another.
        //You can tell which image is in the front and which is behind the other.
        //I'm setting the buttons to be transparent. Setting the buttons to not be transparent will cover the the image below it,
        //that's how I know they're being stacked on top of one another.
        JButton btn1 = gifBtn("Articuno");
        JButton btn2 = gifBtn("Rayquaza");
        c.gridx = 0;
        pnlPokemonInParty.add(btn1, c);
        c.gridx = 1;
        pnlPokemonInParty.add(btn2, c);
        this.add(pnlPokemonInParty);
        this.pack();
        this.setVisible(true);
    }
    public JButton gifBtn(String name)
    {
        final JButton btn = new JButton();
        URL url = null;
        try {
            url = new URL(images.get(name));
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
        Icon icon = new ImageIcon(url);
        btn.setIcon(icon);
        btn.setBackground(new Color(50,50,50,0));
        return btn;
    }

    public static void main(String[] args)
    {
        GameManager gameManager = new GameManager();
    }
}

我可以通过不将JButton的背景色设置为透明来隐藏问题,但这并不能解决问题。

为什么会发生这种情况呢? 我更担心的是两个图像出现在同一个JButton上,但还有另一个问题,当观察图像时,我不知道该如何解释。

1个回答

2
发布问题时建议附上以下格式的MRE
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GameManager extends JFrame{

    private final   Map <String, String> images = new HashMap<>(2);

        GameManager()
        {
            images.put("Articuno", "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/256x256/Box_Green.png");
            images.put("Rayquaza", "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/256x256/Box_Red.png");

            JPanel pnlPokemonInParty = new JPanel(new GridBagLayout());
                GridBagConstraints c = new GridBagConstraints();
                JButton btn1 = gifBtn("Articuno");
                JButton btn2 = gifBtn("Rayquaza");
                c.gridx = 0;
                pnlPokemonInParty.add(btn1, c);
                c.gridx = 1;
                pnlPokemonInParty.add(btn2, c);
                this.add(pnlPokemonInParty);
                this.pack();
                this.setVisible(true);
        }
         public JButton gifBtn(String name)
         {
                final JButton btn = new JButton();
                URL url = null;
                try {
                    url = new URL(images.get(name));
                } catch (MalformedURLException ex) {
                    ex.printStackTrace();
                }
                Icon icon = new ImageIcon(url);
                btn.setIcon(icon);
                btn.setBackground(new Color(50,50,50,0));
                return btn;
         }

    public static void main(String[] args)
    {
            GameManager gameManager = new GameManager();
    }
 }

代码在使用公开可用的图像时运行良好,因此表明存在本地资源问题。
MRE使帮助更加容易,是一个强大的调试工具。在准备MRE时,很多情况下您可能会发现问题。


编辑1:通过新添加的MRE,问题现在变得清晰了:每个按钮都显示两个重叠的图像。
当删除btn.setBackground(new Color(50,50,50,0));时,问题确实消失了。
这可以通过"setBackground()在某些平台上不太好读取"来解释,该链接引用了@trashgod的答案。
根据@Andrew Thompsom的答案所述,可以通过设置LAF来消除此问题。
这里有一个mre演示它。

这取决于外观是否尊重此属性,有些可能会选择忽略它。

(引自 JComponent#setBackground(Color) 文档 documentation。)
编辑 2:
自定义的 JButton 覆盖了 paintComponent 方法,可以正常工作(对于透明度为 0 的颜色,如 new Color(50,50,50,0) 或任何其他颜色,都是透明的)。
class Main extends JFrame{

    private final Map <String, String> images = new HashMap<>();

    Main()
    {
        images.put("Articuno", "https://66.media.tumblr.com/d9105814c15295196a3dbe75c32ba1a0/tumblr_oagpklvBGf1scncwdo1_400.gif");
        images.put("Rayquaza", "https://play.pokemonshowdown.com/sprites/ani-back-shiny/rayquaza.gif");
        images.put("GreenCircle", "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/128x128/Circle_Green.png");
        images.put("RedBox", "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/256x256/Box_Red.png");

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().setBackground(Color.WHITE);
        this.setLayout(new FlowLayout());
        this.add(gifBtn("GreenCircle"));
        this.add(gifBtn("RedBox"));
        this.add(gifBtn("Articuno"));
        this.add(gifBtn("Rayquaza"));
        this.pack();
        this.setVisible(true);
    }

    public JButton gifBtn(String name)
    {
       JButton btn = new CustomButton();
        try {
            URL url = new URL(images.get(name));
            btn.setIcon(new ImageIcon(url));
        } catch (MalformedURLException ex) { ex.printStackTrace();   }

        return btn;
    }

    public static void main(String[] args) throws Exception
    {
        new Main();
    }
}

class CustomButton extends JButton{

    private final Color bgColor = new Color(255,192,203,0);

    public CustomButton() {
        //setBorderPainted(false);  //optioal
        setContentAreaFilled(false);
        setOpaque(false);
    }

    @Override
    public void paintComponent(Graphics g){
        g.setColor(bgColor);
        Rectangle r = g.getClipBounds();
        g.fillRect(r.x, r.y, r.width, r.height);
        super.paintComponent(g);
    }
}

JComponent#setBackground(Color)的文档documentation中指出:
直接继承自JComponent的子类必须重写paintComponent以遵守此属性。这取决于外观和感觉是否尊重此属性,有些可能选择忽略它。
但不知何故,JButton并没有这样做。

你的代码使用了两个大小和形状相同的图片。如果这些图片被输出到两个重叠的按钮上,你将无法分辨它们。我已尝试在你的代码中使用来自网络上的gif图像,但问题仍然存在。感谢你帮助我解决这个问题。 - Bwizz
有了新添加的mre,问题现在变得更加清晰了。请查看我的编辑。 - c0der
你的MRE再次展示了两张大小相同的图片。更具体地说,“它们”大小不同,但由于你使用了边框布局,它会将位于顶部的图像后面的那个图像设置为与顶部的那个图像相同的大小。在用实际大小不同的gif替换这些图片并将它们放入流式布局后,我仍然可以看到问题。除此之外,你提供的解决方案覆盖了按钮的背景,使其不透明,这不是一个解决方案。事实上,它是相反的。我可以通过不使按钮透明来实现这一点。谢谢。 - Bwizz
我的上一条评论已经没有空间了...这篇文章的目标是找出为什么两个按钮都会复制相同的图像。这非常奇怪,不应该发生。这是主要目标。然后就是找到解决方案。我如何将我制作的MRE与新的变化链接起来,展示问题,而不改变帖子本身?https://repl.it/repls/SwelteringMuddyIntelligence 我尝试了你所做的事情,请让我知道它是否有效。谢谢。 - Bwizz
你的MRE再次显示了两个相同大小的图像。不,它并没有。因为你正在使用边框布局。我用GridbagLayout得到的结果完全一样。你提供的MRE确实显示了一个绘画问题,但是不同于这个问题。尝试在不设置LAF的情况下运行它,看看有什么不同。 - c0der

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接