2

I am creating a minesweeper game, and what I want to do is to have a JTextField where the user inputs his name in order for his score to be saved in a file.

My problem is that when I create a JTextField and add it to my Jpanel it appears in 2 locations. Here is an image of what is happening (https://i.stack.imgur.com/6fUtJ.jpg)

This is my code over-simplified. I believe that I don't properly understand something about how the mechanism of the GUI works.

GUI.java

public class GUI extends JFrame {
  //..
  //some variables here
  //...

  public GUI() {
        this.setTitle("Minesweeper Game");
        this.setSize(WIDTH, HEIGHT);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
        this.setResizable(false);
        this.setLayout(null);

        //This method does not involve any drawing, it only places data in some arrays that I later use in paintComponent() to draw stuff accordingly to what the data is
        setMinefield();


        Board board = new Board();
        this.setContentPane(board);

        Click click = new Click();
        this.addMouseListener(click);
    }

    public class Board extends JPanel {
        public void paintComponent (Graphics g) {
        //...    
        //Drawing tiles, smiley, counters
        //...

        //And now I draw the area for the JTextField, and I also create it and add it in the Jpanel
        JTextField textField = new JTextField();
        textField.setFont(new Font("Tahoma", Font.PLAIN, 35));
        textField.setBounds(290, 80, 135, 40); //<-- This correctly places the textField where I want. The second textField seems to appear in the exact center of the X axis of my window

        add(textField); //<-- Adding the textField to the Jpanel

        } //End of paintComponent()
    }//End of Board class
}//End of GUI class

Main.java

public class Main implements Runnable {

    GUI gui = new GUI();

    public static void main(String[] args) {
        new Thread (new Main()).start();
    }

    @Override
    public void run() {
        while (true) {
            gui.repaint();
        }
    }

}
  • What values have WIDTH and HEIGHT? Try to remove setSize(). I tested it and without setSize() it looked much better. – Ralf Renz Jun 06 '19 at 12:55
  • 1
    You shouldn't do anything other than painting components in a paintComponent implementation. – TT. Jun 06 '19 at 13:03
  • 1
    *Under no circumstances* should you add components in a painting method. You don’t control when paintComponent is called. It can be called whenever the user moves the window, and even when the user moves the mouse over the window. Also, don’t call `gui.repaint()` in a tight loop with no sleep calls; you will overwhelm the event queue. – VGR Jun 06 '19 at 13:29
  • 1
    1) For better help sooner, [edit] to add a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). 2) Java GUIs have to work on different OS', screen size, screen resolution etc. using different PLAFs in different locales. As such, they are not conducive to pixel perfect layout. Instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556) along with layout padding and borders for [white space](http://stackoverflow.com/a/17874718/418556). 3) Provide ASCII art or a simple drawing of the *intended* layout of the GUI. – Andrew Thompson Jun 06 '19 at 14:02
  • BTW - only just noticed the [tag:minesweeper] tag. I've seen good implementations of mine sweeper. They don't require custom painting or a thread to constantly repaint the GUI. Here is [an example](https://stackoverflow.com/a/7016492/418556). – Andrew Thompson Jun 06 '19 at 14:20

2 Answers2

2

I think the problem is that you have overridden paintComponent in your Board class. This method gets called every time the component needs to be drawn so a new text field will be added each time.

It would be better to add the text field in the constructor for your board class.

mwarren
  • 759
  • 3
  • 6
  • Thanks for this answer! I did what you said and now I only get 1 textField, the central one. I use the command `textField.setBounds(290, 80, 135, 40);` to place it where I want but it doesn't work. Why could this happen? – kirios kavouris Jun 06 '19 at 14:06
  • I think because you have no layout manager on your GUI frame it is positioning the panel that is your Board. Try setting the bounds on your board object. – mwarren Jun 06 '19 at 14:17
  • Don't use setBounds() and don't use a null layout! – camickr Jun 06 '19 at 14:25
2

I use the command textField.setBounds(290, 80, 135, 40); to place it where I want but it doesn't work. Why could this happen?

Swing was designed to used with layout managers. The default layout manager for a JPanel is the FlowLayout. The FlowLayout will ignore the setBounds(...) statement and set the size/location of the text field based on the rules of the FlowLayout.

So don't attempt to use a null layout and don't use setBounds(). Instead let the layout manager do its job.

Also, you should be adding the components to the frame BEFORE you make the frame visible.

I would suggest your code should be something like:

JTextField textField = new JTextField(10);
JPanel top = new JPanel();
top.add( textField );

Board board = new Board();

add(top, BorderLayout.PAGE_START);
add(board, BorderLayout.CENTER);
setResizable( false );
pack();
setVisible( true );

The Board class should override the getPreferredSize() method of the Board to return your desired size so that the pack() method works properly.

The default layout manager for a JFrame is the BorderLayout. So, now the top part of the frame will contain the text field centered and the main part of the frame will contain your Board class. Read the section from the Swing tutorial on How to Use BorderLayout to understand how the above code works.

Also, the MouseListener should be added to the Board, not the JFrame.

camickr
  • 321,443
  • 19
  • 166
  • 288