【问题标题】:JButton not showing up properly over GIFJButton 无法在 GIF 上正确显示
【发布时间】:2019-09-28 17:53:56
【问题描述】:

我有一个程序,它本质上应该是一些游戏的标题屏幕。背景是 GIF(我不确定这是否会导致问题),我需要一些 JButtons 来运行实际游戏。问题是JButton 有时仅在我将鼠标悬停在它上面时才会出现(并且在那一瞬间),否则它是不可见的。它工作得很好,进入游戏等等,它只是不可见的。

我试图查看问题是否在于我使用的是 GIF 以及 paintComponent() 方法,不过,当我使用 JPEG 时它没有出现。

代码如下:

public class TestingGrounds extends JFrame{
     //declarations
     JButton snakeButton;
     JPanel snakeButtonPanel;
     JFrame window;
     Container con;
     TitleScreenHandler tsHandler = new TitleScreenHandler();
     //constructor
     public TestingGrounds(){
          //main JFrame
          window = new JFrame("Title Screen");
          window.add(new ImagePanel());
          window.setResizable(false);
          window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          window.setSize(1831, 1030);
          window.setVisible(true);
          window.setLocationRelativeTo(null);
          window.setLayout(null);

          con = window.getContentPane();

          //panel for the button to go to the snake game
          snakeButtonPanel =  new JPanel();
          snakeButtonPanel.setBounds(100,100,600,150);

          //button to go to snake game
          snakeButton = new JButton("Snake");
          snakeButton.setBackground(Color.BLACK);
          snakeButton.setForeground(Color.WHITE);
          snakeButton.setFont(new Font("Times New Roman", Font.ITALIC, 30));
          snakeButton.addActionListener(tsHandler);
          snakeButton.setFocusPainted(false);
          //adding button to panel
          snakeButtonPanel.add(snakeButton);
          //adding panel to container
          con.add(snakeButtonPanel);
          //setting the panel as visible
          snakeButtonPanel.setVisible(true);

     }
     //main method for running constructor
     public static void main(String[] args) {
          new TestingGrounds();
     }

     //what to do if the button is pressed
     public class TitleScreenHandler implements ActionListener {
          public void actionPerformed(ActionEvent event){
               //goes to main game screen if start button is pressed
               new SnakeGame();
          }
     }
}
//class for using the gif
class ImagePanel extends JPanel {
     Image image;
     public ImagePanel() {
          image = Toolkit.getDefaultToolkit().createImage("C:/Users/eklut/Desktop/Coding/ICS4U1/src/graphicsstuff/snek/source.gif");
     }

     public void paintComponent(Graphics g) {
          super.paintComponent(g);
          g.drawImage(image, 0, 0, this);
     }
}

抱歉,我知道这是相当多的代码,但我觉得我必须展示所有这些,因为我不确定问题出在哪里。

我预计按钮会显示在 gif 上,但它几乎似乎正在以相反的方式发生

【问题讨论】:

  • “我不确定这是否会导致问题” 您是否尝试过删除 GIF?但是 1+ 对于几乎完整的minimal reproducible example,我们无法访问您的图片,请使用来自互联网链接的图片。
  • @Frakcool “我们无权访问您的图片,请使用来自 Internet 链接的图片。” 有多种图片类型和尺寸可用于 hot-来自this Q&A 的链接,我为此特定目的放在一起。例如。 This answer 指向嵌入在 this question 中的图像的热链接。
  • @AndrewThompson 那是我正在寻找的链接,但不记得是你的链接还是 camickr 的问答,当我写那个评论时,我脑子里正是那个帖子,谢谢分享跨度>

标签: java swing user-interface awt


【解决方案1】:

此 MCVE 包含如此多的更改,最好通过代码并查看 cmets。

请注意,代码使用 动画 GIF 作为 BG。我会说这是用来明确证明它是一个 GIF,但事实是示例图像的页面只包含动画 GIF。鉴于 PNG 将支持半透明和比 GIF 更多的颜色,这种格式对其他任何东西都没有多大好处。

import java.awt.*;
import java.awt.event.*;
import java.net.MalformedURLException;
import javax.swing.*;
import java.net.URL;

//public class TestingGrounds extends JFrame {
public class TestingGrounds {

    //declarations
    JButton snakeButton;
    JPanel snakeButtonPanel;
    JFrame window;
    TitleScreenHandler tsHandler = new TitleScreenHandler();

    //constructor
    public TestingGrounds() {
        //main JFrame
        window = new JFrame("Title Screen");
        try {
            JPanel imagePanel = new ImagePanel();
            imagePanel.setLayout(new BorderLayout());
            window.add(imagePanel);
            //window.setResizable(false);
            window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            // Don't guess sizes needed. They will be wrong on other 
            // PLAFs or platforms.
            //window.setSize(850, 600);

            // should be last
            //window.setVisible(true);
            window.setLocationRelativeTo(null);
            //window.setLayout(null);

            //con = window.getContentPane();
            //panel for the button to go to the snake game
            snakeButtonPanel = new JPanel(new GridBagLayout());
            snakeButtonPanel.setOpaque(false);
            //snakeButtonPanel.setBounds(100, 100, 600, 150);

            //button to go to snake game
            snakeButton = new JButton("Snake");
            snakeButton.setMargin(new Insets(10,40,10,40));
            snakeButton.setBackground(Color.BLACK);
            snakeButton.setForeground(Color.WHITE);
            snakeButton.setFont(new Font("Times New Roman", Font.ITALIC, 30));
            snakeButton.addActionListener(tsHandler);
            snakeButton.setFocusPainted(false);
            //adding button to panel
            snakeButtonPanel.add(snakeButton);
            //adding panel to container
            //con.add(snakeButtonPanel);
            // Adding the container to imagePanel
            imagePanel.add(snakeButtonPanel);

            //setting the panel as visible
            snakeButtonPanel.setVisible(true);

            window.pack();
            window.setVisible(true);
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
    }

    //main method for running constructor
    public static void main(String[] args) {
        new TestingGrounds();
    }

    //what to do if the button is pressed
    public class TitleScreenHandler implements ActionListener {

        public void actionPerformed(ActionEvent event) {
            //goes to main game screen if start button is pressed
            //new SnakeGame();
            JOptionPane.showMessageDialog(snakeButton, "Snake Game");
        }
    }
}
//class for using the gif

class ImagePanel extends JPanel {

    Image image;

    public ImagePanel() throws MalformedURLException {
        image = Toolkit.getDefaultToolkit().createImage(
                new URL("https://i.stack.imgur.com/OtTIY.gif"));
        MediaTracker mt = new MediaTracker(this);
        mt.addImage(image, 1);
        try {
            mt.waitForAll();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

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

    @Override
    public Dimension getPreferredSize() {
        // this allows us to pack() the window around the image
        return (new Dimension(image.getWidth(this), image.getHeight(this)));
    }
}

【讨论】:

  • 谢谢您(以及所有提供帮助的人)!我非常感激。我只是想知道如何在TestingGrounds 构造函数中使用Dimension 来获得适合GIF 的大小。正如你所说,我不想猜测尺寸。
  • “我只是想知道如何在 TestingGrounds 构造函数中使用 Dimension 来获得正确的 GIF 大小。” 它真的不需要知道,因为 (我想)我已经在示例中进行了演示。 ImagePanel 返回 GIF 的大小作为其首选大小,然后在 TestingGrounds 中调用 pack() 调整框架大小以适合面板。
【解决方案2】:

我尚未测试您的代码,但您需要更改以下内容:

window.setVisible(true);
window.setLayout(null);

这两行会给你带来问题。为什么?

  1. 第一个需要调用 AFTER,您将所有组件添加到 window 框架中,而不是之前(即程序中的最后一行)。这可能是您解决问题的方法。

  2. 第二个...null layoutis Evil!frowned upon。请查看How to use a Layout Manager,您甚至可以将它们组合起来。这就是if you use null layout 发生的情况,并在具有不同操作系统/PLAF/屏幕分辨率等的不同计算机上运行您的程序。

  3. 无需调用:

    snakeButtonPanel.setVisible(true);
    
  4. 根据我上面的评论,如果您不确定您的问题是否与 GIF 相关,请删除 GIF 并对其进行测试,如果不相关,请将其从您的问题中删除,如果是的,保留它。

【讨论】:

  • 通常我百分之一地同意 null 布局,但游戏可能是该规则的一个例外。
  • 会记住这一点@VGR 感谢您的建议。我还有东西要学
  • @VGR “但游戏可能是该规则的例外” 我不同意。任何用于定位组件的逻辑都应该封装在(可能是自定义的)布局中。
  • “这就是发生的事情..” 因为我已经对这个答案投了赞成票,所以我决定增加赏金。必须将其作为快速视觉指南添加书签,说明使用null 布局的一个原因。一张图描绘一千个字。
  • @EkagraLuthra, but the gif doesn't show up at all. - 你被告知了这样做的原因。您没有阅读所有答案吗?
【解决方案3】:

除了@Fracool 的建议:

你这样做:

window.add(new ImagePanel());

等同于:

window.getContentPane().add(new ImagePanel());

稍后再做:

con = window.getContentPane();
...
con.add(snakeButtonPanel);

这意味着您将“imagePanel”和“snakeButtonPanel”添加到内容窗格中。

当多个组件被添加到同一个面板时,最后添加的组件首先被绘制。所以在这种情况下,首先绘制“snakeButtonPanel”,然后绘制“imagePanel”。您不想将两个组件添加到内容窗格。您希望背景图像和按钮之间具有父/子关系。

另外,不需要“snakeButtonPanel”,直接添加“snakeButton”即可。

所以,而不是像这样的结构:

- contentPane
    -imagePanel
    - snakeButtonPanel
        - snakeButton

你应该有这样的结构:

- contentPane
    -imagePanel
        - snakeButton

基本代码如下:

JButton snakeButton = new JButton(...);

JPanel imagePanel = new ImagePanel();
imagePanel.add( snakeButton );

window.add( imagePanel );

如果您保留默认布局管理器(即 BorderLayout),则图像面板将填充整个框架,并且按钮将根据 FlowLayout 的规则定位在图像面板上,这是默认布局管理器一个JPanel。

public class TestingGrounds extends JFrame{

不要扩展 JFrame。您没有向 JFrame 类添加新功能,并且构造函数中的代码会创建一个 JFrame,这是您要向其中添加所有组件的框架。

【讨论】:

    猜你喜欢
    • 2019-09-14
    • 1970-01-01
    • 2023-04-09
    • 2021-02-22
    • 2021-12-26
    • 2018-07-21
    • 2017-03-01
    • 1970-01-01
    • 2022-08-16
    相关资源
    最近更新 更多