0

So I have been coding Java for a few months now, and I have been using JOptionPane to display text and variables in my games. I want to upgrade to a single window like a normal game, but I want to only focus on simple buttons and text on the screen. I have tried learning JFrame and ActionListsner, but I failed to completley figure it out. JFrame really confused me.

My question is this: Is there an easier way beside JFrame to just have a window that I can have simple text, buttons and TextFields without the hassle of opening a bunch of windows with JOptionPane, making crap loads of ActionListeners with JFrame or having to get into GUI? If not, where can I find help on how to make games with JFrame?

Vince
  • 14,470
  • 7
  • 39
  • 84

3 Answers3

5

You should be using a JFrame. Trust me, they aren't that hard to use. Using a JFrame, you could create multiple panels and switch between them using CardLayout.

Since you said you aren't sure about how JFrame works, I gave a short introduction to them at the end of this post. But first, lets first talk about how to solve your problem.


Switching Panels via CardLayout

When you want to switch whats being viewed in the window completely, you're gonna want an entirely different panel for that specific purpose (for example, one for Main Menu, and one for the game). CardLayout was created for this.

You specify a "deck" panel. This panel will hold all the other panels you wanna switch between (cards):

CardLayout layout = new CardLayout();
JPanel deck = new JPanel();
deck.setLayout(layout);

You'll need to maintain a reference to the layout (via a variable) so you can switch between panels.

Now that we have a "deck" panel, we need to add some cards. We do this by creating more JPanels, and specifying a name when we add it to the frame (constraints):

JPanel firstCard = new JPanel();
JPanel secondCard = new JPanel();
deck.add(firstCard, "first");
deck.add(secondCard, "second");

The first card added to the deck will always be the first one to show.

Once you have all your cards added, you can switch between them by calling layout.show(deck, "second");. This is how you use CardLayout to manage multiple panels within your container.


Creating listeners

There's no easier way to manage it. It only gets harder from there (bindings - I highly suggest you look into them). For listeners, there are 2 steps:

  1. Create the listener
  2. Add it to the component

Could be 1 if you created the listener "on the fly" using a lambda:

JButton button = new JButton();

//Java 8+
button.addActionListener(event -> {
    //whenever you click the button, code in here triggers
});

For those who don't use Java 8, you will need to use an anonymous class:

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        //whenever you click the button, code in here triggers
    }
});

Only some components support ActionListener. Anything that extends AbstractButton, like JMenuItem, JButton, JRadioButton, and more will support ActionListeners. JTextField also supports it, even though it's not an AbstractButton. Every component supports KeyListener, though, which can also be used to listen for input events from the user.

If you have any questions about this, let me know and I'll be glad to answer them.


Using Swing Components

JFrame

You initialize a JFrame:

JFrame frame = new JFrame();

You then want to set the defaultCloseOperation, to determine what happens when the window closes: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

There are different options, but EXIT_ON_CLOSE will terminate your program after closing the window. If you do not set the defaultCloseOperation, then your window will close, but your program will still be running. BIG NO NO. If you don't want the entire program to exit when you close the frame, use DISPOSE_ON_CLOSE.

After you have the close operation, you might be tempted to set the size. This is bad practice. Your frame should size depending on what's inside of it. To do this, you add your components to your frame, then call pack():

JButton button = new JButton("Button");
frame.add(button);
//add other components
frame.pack();

This will ensure your frame sizes to fit what's inside of it. You should always design GUI from the inside out, to ensure you always have enough room for what you need.

Containers

JFrame is a Container. Containers are just components that hold other components. All containers should have a LayoutManager to manage how components are laid out and, if needed, sized. JFrame isn't the only container though. JPanel is actually a container that's meant to be added to another container (or window). You can add things to a JPanel, then add that panel to another container. This is how you keep things neatly organized. Containers are pretty straight forward, so there's not much to talk about.

Content Pane

When you create a JFrame, it comes along with something called the contentPane. It is a JPanel nested within the JFrame. When you do frame.add(button), you'll notice that add actually refers to the contentPane:

//in Container class
public Component add(Component comp) {
    addImpl(comp, null, -1); //where it's added
    return comp;
}

//In JFrame class (overriding)
protected void addImpl(Component comp, Object constraints, int index) {
    if(isRootPaneCheckingEnabled()) {
        getContentPane().add(comp, constraints, index); //adds to content pane instead
    } else {
        super.addImpl(comp, constraints, index); //if root panes were not supported, which they currently are
    }
}

You too can grab the contentPane from the frame using

Container pane = frame.getContentPane();

The reason why the contentPane is in Container form is to ensure a strong API (if JPanels were no longer going to be used for this, we wouldn't need to worry about changing the method type of getContentPane). Although, since it IS a JPanel, you can cast it back to it's original form:

JPanel pane = (JPanel) frame.getContentPane();

Although it's usually not needed. Using it as a Container tends to work just fine.

Layouts

JFrame, by default, uses BorderLayout, but you can change this by calling the setLayout method of the container:

FlowLayout layout = new FlowLayout();
frame.setLayout(layout);

Before jumping into layouts, I want to mention that JPanels use FlowLayout as default, except for the frame's default contentPane, which you can also change by doing frame.setContentPane(...). The contentPane uses BorderLayout as default.

Now, lets talk about a couple, starting with the JFrame default: BorderLayout.

Some layouts require what are called constraints, which tell the layout how to handle that specific component that's being added. You specify these constraints when you add the component to the container:

frame.add(button, BorderLayout.SOUTH);

BorderLayout is pretty simple. You have 5 options: NORTH, SOUTH, EAST, WEST, or CENTER. (there are other values such as PAGE_START, which are interchangeable. It's best to just use the easier form)

All constraints for BorderLayout are static field variables that you call similar to how I did. BorderLayout is actually an easy layout to use, seeing how there's not much to it. Even though it's simplicity limits what you can do (you can only put it in a certain position like NORTH), you'd usually combine layouts to get the result you want. This layout can be very powerful when combined with other layouts.

FlowLayout is pretty straight forward, as well as other layouts out there. One of the less straight-forward ones would be GridBagLayout, which can be a really flexible layout. It can also be pretty complex, though, as the documentation even states.

When using GridBagLayout, you need to create a constraints object: GridBagConstraints. To work with GridBagLayout, you set the constraints using the constraints object, then add both the component and the constraints to the container:

 frame.setLayout(new GridBagLayout());
 GridBagConstraints gbc = new GridBagConstraints();

 JButton button = new JButton("yoyoyo");
 frame.add(button, gbc);

Even if we don't adjust the constraints, we MUST still specify it when adding a component to the container.

Lets say we had 4 buttons, and we wanted to put them side by side. You would adjust the constraint's gridx and gridy values:

JButton button = new JButton("1");
JButton button2 = new JButton("2");
JButton button3 = new JButton("3");
JButton button4 = new JButton("4");

frame.add(button, gbc);

gbc.gridx = 1; //or gridx += 1, or gridx = 1. gridx starts at 0
frame.add(button2, gbc);

gbc.gridx = 0; //must reset the value back to normal
gbc.gridy = 1;
frame.add(button3, gbc);

gbc.gridx = 1;
//since gridy already == 1, no need to change it
frame.add(button4, gbc);

We can use the same constraints object, as long as we reset values when needed.

GridBagLayout starts centered, and works from the center out, unless you specify otherwise. You cannot skip grid spaces either. Also, as you'll notice, all your buttons will be touching. If you wanted a little space between each component, you can set the insets of the constraints:

int top = 5, left = 5, bottom = 1, right = 1; 
gbc.insets.set(top, left, bottom, right);

There is a LOT more to this layout, and sadly I just don't feel this is the best place to give the explanation to it all, seeing how it's already documented (I even added the link).

There are many other layouts out there. Get familiar with as many as you possibly can, then find the ones that'll help suit your needs. Positioning should ALWAYS rely on the LayoutManager that's being used. As for sizing, that kinda depends on the layout you're using.

Vince
  • 14,470
  • 7
  • 39
  • 84
0

I believe CardLayout is what you're looking for. With it, the programmer can choose which JPanels should be visible in the JFrame. Upon user interaction you can switch the contents of the JFrame without new JFrames or JOptionPanes popping up.

Is there an easier way than using Swing for simple games? Swing has a learning curve, but with the right resources and practice you can learn to build simple GUI applications pretty quickly.

yts
  • 1,890
  • 1
  • 14
  • 24
  • nonono, I don't want the player to have to switch between different tabs manually, I want it to be like an average game where you click a button and it takes you to a different menu. I also don't want them to be ABLE to switch to menus in a game through tabs –  Dec 03 '14 at 05:04
  • The CardLayout isn't a tabbed layout. You change switch the visible JPanels in the JFrame based on user interaction. – yts Dec 03 '14 at 05:06
  • @pebble225 "You" meaning the programmer, not the user. Practically any GUI application framework will involve something like ActionListeners. If you're having trouble with them you just need more practice :) – yts Dec 03 '14 at 05:08
  • @pebble225 unfortunately, no. I can't suggest any more than googling for articles or finding a book on Swing which covers it. – yts Dec 03 '14 at 05:15
  • Oracle have a [CardLayout tutorial](https://docs.oracle.com/javase/tutorial/uiswing/layout/card.html). Here is a [similar example for JavaFX](http://stackoverflow.com/questions/18619394/loading-new-fxml-in-the-same-scene) in case you switch to that UI framework. – jewelsea Dec 03 '14 at 23:34
0

I would highly recommend using JavaFX. It's a very nice fairly easy to use GUI system with nice looking and customizable controls. JavaFX events (basically ActionListeners) are pretty straightforward as well.

This should get you started: http://docs.oracle.com/javase/8/javafx/get-started-tutorial/get_start_apps.htm#JFXST804

Nikolai97
  • 212
  • 1
  • 2
  • 10
  • This doesn't even answer the question. Question: "*How do I upgrade to a single window?*", Your answer: "*I recommend using JavaFX*". JavaFX allows multiple windows aswell. – Vince Mar 21 '16 at 02:07
  • The OP asked for an easier way than the 'JFrame' solution, so i recommended JavaFX, which is in my opinion easier. I would also like to point out that JavaFX is geared towards a single window, more so than Swing anyways. – Nikolai97 Mar 21 '16 at 02:58
  • You don't show him *how* to achieve this in FX. Those reading this in the future will still not understand how to solve the problem. – Vince Mar 21 '16 at 02:59
  • Yes, i will agree with that. I answered this question before I was very familiar with the site. If I find the time, I will expand this question to get a reader started with the basics of JavaFX. – Nikolai97 Mar 21 '16 at 03:03