1

I have a program that's essentially supposed to be a title screen for a few games. The background is a GIF (I'm not sure if that's contributing to the problem), and I need to have some JButtons that allow for me to run the actual games. The problem is that the JButton only shows up sometimes when I hover over it (and for a split second, at that), otherwise it's invisible. It works just fine, goes to the game and all, it's just invisible.

I've tried to see if the problem is the fact that I'm using a GIF, as well as the paintComponent() method, although, it just didn't show up when I used a JPEG.

Here's the code:

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);
     }
}

Sorry, I know it's a fair amount of code, but I feel like I have to show all of it as I'm not exactly sure where the problem stems from.

I expected the button to show up over the gif, but it almost seems like it's happening the other way around

EkLuthra
  • 147
  • 1
  • 9
  • 2
    *"I'm not sure if that's contributing to the problem"* Have you tried removing the GIF? However 1+ for an almost complete [mcve], we don't have access to your image, please use one from an internet link. – Frakcool May 10 '19 at 20:00
  • 1
    @Frakcool *"we don't have access to your image, please use one from an internet link."* There are a variety of image types & sizes available for hot-linking from [this Q&A](http://stackoverflow.com/q/19209650/418556) that I put together for this specific purpose. E.G. [This answer](https://stackoverflow.com/a/10862262/418556) hot links to an image embedded in [this question](https://stackoverflow.com/q/10861852/418556). – Andrew Thompson May 11 '19 at 02:02
  • 1
    @AndrewThompson that was the link I was looking for but couldn't remember if it was yours or camickr's Q&A, when I wrote that comment I had exactly that post in mind, thanks for sharing it – Frakcool May 11 '19 at 02:05

3 Answers3

3

This MCVE includes so many changes it's better to go through the code and look at the comments.

enter image description here

Note that the code uses an animated GIF as the BG. I would say that was used to explicitly demonstrate that it was a GIF, but the truth is that the page of example images only contains animated GIFs. The format is not much good for anything else, given a PNG will support translucency and a lot more colors than a 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)));
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Thank you (and everyone else that helped)! I greatly appreciate it. I'm just wondering how I can use `Dimension` in the `TestingGrounds` constructor to get the right size for the GIF. As you said, I wouldn't want to be guessing sizes. – EkLuthra May 12 '19 at 22:53
  • 1
    *"I'm just wondering how I can use Dimension in the TestingGrounds constructor to get the right size for the GIF."* It really does not need to know, as (I thought) I'd demonstrated in the example. The `ImagePanel` returns the size of the GIF as its preferred size, then a call to `pack()` in `TestingGrounds` sizes the frame to suit the panel. – Andrew Thompson May 13 '19 at 01:10
1

I haven't tested your code but here are some things you need to change:

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

Those 2 lines are going to give you problems. Why?

  1. The first one needs to be called AFTER you've added all the components to your window frame, not before (i.e. last line in your program). This is probably how you can solve your problem.

  2. The second one... null layout is Evil! and frowned upon. Please take a look at How to use a Layout Manager you can even combine them. This is what happens if you use null layout and run your program in a different computer with different OS / PLAFs / screen resolution / etc.

  3. There's no need to call:

    snakeButtonPanel.setVisible(true);
    
  4. As per my comment above, if you're unsure if your problem is related or not to a GIF, then remove the GIF and test it, if it's not related, remove it from your question at all, if it is, keep it.

Frakcool
  • 10,915
  • 9
  • 50
  • 89
  • Normally I agree one thousand percent about null layouts, but games may be an exception to that rule. – VGR May 10 '19 at 20:51
  • Will keep it in mind @VGR thanks for that suggestion. I still have things to learn – Frakcool May 10 '19 at 21:06
  • @VGR *"but games may be an exception to that rule"* I disagree. Whatever logic is used to position components should be encapsulated in a (possibly custom) layout. – Andrew Thompson May 11 '19 at 01:59
  • 1
    *"This is what happens.."* Since I'd already upvoted this answer, I decided to add a bounty. Must bookmark it as a quick visual guide as to one reason **not** to use `null` layout. A picture paints a thousand words. – Andrew Thompson May 11 '19 at 02:46
  • I tried calling `window.setVisible(true)` directly after all the components have been added, but the gif doesn't show up at all. I also tried calling it at the very end of the `TestingGrounds()` constructor, same result – EkLuthra May 12 '19 at 22:07
  • And what about the rest of the tips? @EkagraLuthra – Frakcool May 12 '19 at 22:19
  • I've gotten rid of the null layout, and am learning how to use a Layout manager. I'm no longer calling `snakeButtonPanel.setVisible(true)`. I figured out that the problem was not with the fact that I was using a GIF, it would have occurred with any image. And of course, I'm calling `window.setVisible(true)` at the end, right after `window.pack()` – EkLuthra May 12 '19 at 23:10
  • 1
    @EkagraLuthra, `but the gif doesn't show up at all.` - and you were given the reason for that. Did you not read all the anwers? – camickr May 13 '19 at 03:40
  • @cmickr I posted that comment after trying your structure `- contentPane -imagePanel - snakeButton` – EkLuthra May 13 '19 at 12:50
  • You missed an `a` in @camickr, in any case you've already accepted an answer which means your problem is solved. If you're having problems with the new code, ask another question – Frakcool May 13 '19 at 13:29
1

In addition to @Fracool's suggestions:

You do:

window.add(new ImagePanel());

which is the same as:

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

Later you do:

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

Which means you are adding the "imagePanel" and "snakeButtonPanel" to the content pane.

When multiple components are added to the same panel, the last component added is painted first. So in this case the "snakeButtonPanel" is painted first and then the "imagePanel" is painted. You don't want to add two components to the content pane. You want to have a parent/child relationship between the background image and the button.

Also, there is no need for the "snakeButtonPanel", you can add the "snakeButton" directly.

So, instead of having a structure like:

- contentPane
    -imagePanel
    - snakeButtonPanel
        - snakeButton

You should have a structure like:

- contentPane
    -imagePanel
        - snakeButton

And the basic code would be like:

JButton snakeButton = new JButton(...);

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

window.add( imagePanel );

If you keep the default layout manager (which is a BorderLayout) then the image panel will fill the entire frame and the button will be positioned on the image panel based on the rules of the FlowLayout, which is the default layout manager for a JPanel.

public class TestingGrounds extends JFrame{

Don't extend JFrame. You are not adding new functionality to the JFrame class and the code in your constructor creates a JFrame, which is the frame you are adding all the components to.

camickr
  • 321,443
  • 19
  • 166
  • 288