CardLayout, in JFrame or JPanel? - java

I have to make a game for school and I've been having some trouble with switching JPanels with a click on the JButton. I want to use a CardLayout, but I'm new to Java which makes it very hard. My goal is to have all my Panels in different classes, like class 'Panel 1', class 'Panel 2' etc. (instead of creating my JPanels in my main (JFrame) class, so my code is easier to read. Is it possible to put your CardLayout container in the class which contains my JFrame? And also, where do I put that darn ActionPerformed? Here is my code, hope you guys can help me!
MAIN (JFrame) CLASS
package invers;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class InversMain extends JFrame implements ActionListener
{
public CardLayout cardlayout;
public Container contentPane = this.getContentPane();
public InversMain()
{
JFrame frame = new JFrame();
frame.setLayout(cardlayout);
frame.setSize(1366,768);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Invers");
frame.setResizable(true);
frame.setVisible(true);
contentPane.setPreferredSize(new Dimension(600, 400));
contentPane.add(new InversMainPaneel(), "Panel 1");
contentPane.add(new InstellingenPaneel(), "Panel 2");
settingsButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cardlayout.show(contentPane, "Panel 1");
}
});}
public static void main ( String [] args)
{
new InversMain();
}
}
Note that the settingsButton is my button from the PANEL 1 class. Because it isn't created in my main class, it gives an error. I want to refer to my settingsButton from PANEL 1 class, from within my main class. Is this possible?
PANEL 1, PANEL CONTAINING MY BUTTONS, THIS IS MY MAIN MENU PAGE
package invers;
import java.awt.Color;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JPanel;
public class InversMainPaneel extends JPanel
{
private JButton nieuwSpelKnop = new JButton("Nieuw spel");
private JButton laadSpelKnop = new JButton("Laad Spel");
private JButton settingsButton = new JButton("Settings");
private JButton handleidingKnop = new JButton("Handleiding");
public InversMainPaneel()
{
this.setLayout(null);
nieuwSpelKnop.setSize(300,40);
nieuwSpelKnop.setFont(new Font("Arial", Font.BOLD, 25));
nieuwSpelKnop.setLocation(520,250);
nieuwSpelKnop.setVisible(true);
laadSpelKnop.setSize(300,40);
laadSpelKnop.setFont(new Font("Arial", Font.BOLD, 25));
laadSpelKnop.setLocation(520,350);
laadSpelKnop.setVisible(true);
settingsButton.setSize(300,40);
settingsButton.setFont(new Font("Arial", Font.BOLD, 25));
settingsButton.setLocation(520,450);
settingsButton.setVisible(true);
handleidingKnop.setSize(300,40);
handleidingKnop.setFont(new Font("Arial", Font.BOLD, 25));
handleidingKnop.setLocation(520,550);
handleidingKnop.setVisible(true);
this.add(nieuwSpelKnop);
this.add(laadSpelKnop);
this.add(settingsButton);
this.add(handleidingKnop);
this.setBackground(new Color(178,143,79));
}
}
}
PANEL 2, FOR TESTING IF THE CARDLAYOUT WORKED
package invers;
import java.awt.Color;
import javax.swing.JPanel;
public class InstellingenPaneel extends JPanel
{
public InstellingenPaneel()
{
this.setBackground(new Color(178,143,79));
}
}

Have you tried setting the JFrame contentpane using the setContentPane method?
Because I can see you declaring JFrame and a contentpane object, but not setting it, or setting it with your panels.

Note that the settingsButton is my button from the PANEL 1 class. Because it isn't created in my main class, it gives an error.
I'm guessing that the error you're getting is telling you that cardlayout is referenced in an inner class and thus must be made final. To fix this problem, simply insert the final keyword on your creation of cardlayout.
Second - yes, it is perfectly acceptable (and in line with best practices) to define your panel types in separate classes and then create instances of those classes to place in your JFrame.
Third, it appears that you have "that darned actionPerformed" in the right place (i.e. as a method in your ActionListener inner-class), but you should add the #Override annotation to it. Do you know what I mean by that?
Finally, if you want to refer to the settingsButton from another class, you have several options. I would recommend declaring settingsButton as an instance variable of your JFrame class, and passing a reference to the JFrame to the constructor of the InversMainPanel class:
public InversMainPanel(InversMain im) {
...
im.settingsButton. //do something with the settings button.
having created an InversMainPanel from the InversMain class like this:
InversMainPanel imp = new InversMainPanel(this)
with this referring to the InversMain instance from which the call is being made.
Let me know if I can explain any of this further.
PS: Check out this tutorial on Java naming conventions

Related

JButton and JTextField

What's wrong? ImageIcon and the frame's size are working properly.
But the JTextField and the JButton aren't.
I need the solution.
import javax.swing.*;
import javax.swing.ImageIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Frame {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Alkalmazás");
frame.setVisible(true);
frame.setSize(500,500);
frame.setResizable(false);
JTextField field = new JTextField();
field.setBounds(40,250, 300,35);
JButton button = new JButton(new ImageIcon("table.png"));
button.setBounds(40,400, 250,25);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
tf.setText(""something);
}
});
frame.add(field);
frame.add(button);
}
}
You didn't mention what's "not working properly", but there are a few errors with your code:
Don't call your class Frame, it may confuse you or others about java.awt.Frame, something that may work would be MyFrame
Right now all your class is inside the main method and it's not placed inside the Event Dispatch Thread (EDT), to fix this, create an instance of your class and call a method createAndShowGUI (or whatever you want to name it) inside SwingUtilities.invokeLater()
For Example:
public static void main(String args[]) {
SwingUtilities.invokeLater(new MyFrame()::createAndShowGUI)
}
Or if using Java 7 or lower, use the code inside this answer in point #2.
setVisible(true) should be the last line in your code, otherwise you may find some visual glitches that may be resolved until you move your mouse above your window or something that triggers the call to repaint() of your components.
Instead of calling setSize(...) directly, you should override getPreferredSize(...) of your JPanel and then call pack() on your JFrame, see this question and the answers in it: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
You're adding 2 components to the CENTER of BorderLayout, which is a JFrame's default layout manager, there are other layout managers and you can combine them to make complex GUI's.
setBounds(...) might mean that you're using null-layout, which might seem like the easiest way to create complex layouts, however you will find yourself in situations like this one if you take that approach, it's better to let Swing do the calculations for you while you use layout managers. For more, read: Why is it frowned upon to use a null layout in Swing?
With all the above tips now in mind, you may have a code similar to this one:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class MyFrame {
private JFrame frame;
private JPanel pane;
private JTextField field;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MyFrame()::createAndShowGUI);
}
private void createAndShowGUI() {
frame = new JFrame("Alkalmazás");
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
field = new JTextField(10);
button = new JButton("Click me");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
field.setText("something");
}
});
pane.add(field);
pane.add(button);
frame.add(pane);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Now you have an output similar to this one:
What about you want the JTextField to have a more "normal" size? Like this one:
You'll have to embed field inside another JPanel (with FlowLayout (the default layout manager of JPanel)), and then add that second JPanel to pane, I'm not writing the code for that as I'm leaving that as an exercise to you so you learn how to use multiple layout managers

Attempting to set the layout to BoxLayout

I can't seem to find a solution online for why I'm getting this error on attempted run
I'm working on making a simple test system for a different program when are button press will yield value in a text box. I would like them to be on different lines to make it cleaner, so I looked into layouts. I decided a Box Layout would fit me best. I looked at different examples before attempting this and my code ended up looking like this, (apologies for the messy code)
Update
Got the box layout error to disappear but the code will not center them on the panel/frame. The label and button align left while the textfield becomes very large. I don't need it todo that
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import static javax.swing.BoxLayout.Y_AXIS;
import static javax.swing.SwingConstants.CENTER;
public class button extends JFrame {
static JFrame f;
static JButton b;
static JLabel l;
// main class
public static void main(String[] args)
{
// create a new frame to stor text field and button
f = new JFrame("panel");
BoxLayout layout = new BoxLayout(f, BoxLayout.Y_AXIS);
f.setLayout(layout);
// create a label to display text
l = new JLabel("panel label");
b = new JButton("button1");
JTextField textArea = new JTextField(5);
textArea.setEditable(false);
//textArea.append("Hello World");
// create a panel to add buttons
JPanel p = new JPanel();
// add buttons and textfield to panel
f.add(p);
f.setSize(300, 300);
p.add(l);
p.add(b);
p.setBackground(Color.white);
p.add(textArea);
f.show();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Random r = new Random();
textArea.setText(String.valueOf(r));
}
});
}
}
Error
Exception in thread "main" java.awt.AWTError: BoxLayout can't be shared
at java.desktop/javax.swing.BoxLayout.checkContainer(BoxLayout.java:461)
at java.desktop/javax.swing.BoxLayout.invalidateLayout(BoxLayout.java:245)
at java.desktop/javax.swing.BoxLayout.addLayoutComponent(BoxLayout.java:278)
at java.desktop/java.awt.Container.addImpl(Container.java:1152)
at java.desktop/java.awt.Container.add(Container.java:1029)
at java.desktop/javax.swing.JFrame.addImpl(JFrame.java:553)
at java.desktop/java.awt.Container.add(Container.java:436)
at button.main(button.java:36)
I would like the three items to all to be stacked one on top of another with a space between them. The order doesn't matter right now.
Swing was first added to the JDK in 1998 and has undergone a lot of changes since. Unfortunately, when you read Web pages about Swing, it is not obvious when that page was last updated. Consequently you may be learning outdated techniques for writing Swing code.
First of all, according to the code you posted, class button does not need to extend class JFrame since you use a static variable as your application's JFrame. Also, JFrame is a top-level container which makes it a special kind of container and not the same kind of continer as a JPanel. You need to set the layout manager for your JPanel and then add the JLabel, JTextField and JButton to that JPanel. And then add the JPanel to the JFrame.
Calling method pack() of class JFrame will automatically set the preferred sizes for the components inside the JFrame. It appears in the code below.
Please also look at Java coding conventions which allows others to more easily read and understand your code. And note that, according to these conventions, I renamed your class from button to Buttons and also because there are already several class in the JDK named Button.
Here is my rewrite of your code...
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
public class Buttons implements Runnable {
public void run() {
createAndShowGui();
}
private void createAndShowGui() {
JFrame f = new JFrame("Box");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel p = new JPanel();
BoxLayout layout = new BoxLayout(p, BoxLayout.Y_AXIS);
p.setLayout(layout);
JLabel l = new JLabel("panel label");
JTextField textField = new JTextField(5);
JButton b = new JButton("button1");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Random r = new Random();
textField.setText(String.valueOf(r.nextBoolean()));
}
});
p.add(l);
p.add(textField);
p.add(b);
f.add(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
public static void main(String[] args) {
Buttons instance = new Buttons();
EventQueue.invokeLater(instance);
}
}

Basic GUI Swing - components not showing

Here is my code snippet containing child JButton and JPanel objects but it's not working. And it's not showing any compilation errors in Eclipse.
import java.awt.FlowLayout;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class gui extends JFrame implements ActionListener {
private JButton b;
private TextField c;
private JLabel l;
private String sn;
// Constructor for making framework
public gui() { setLayout(new FlowLayout());
JFrame f=new JFrame("Hello!");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.setSize(200,200);
f.setTitle("GUI");
b=new JButton("Click");
l=new JLabel("Enter Name");
c=new TextField("Enter..",10);
c.setEditable(true);
l.setBounds(20,20,20,20);
f.setBounds(10, 10, 10, 10);
b.addActionListener(this);
add(b);
add(f);
add(l);
add(c);
}
public static void main(String[] args) {
gui g=new gui();
g.setVisible(true);
} //main method
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Working");
}
}
Your class "is a" GUI and then you also create a new JFrame, so you really have two frames in your code.
However the frame you make visible does not have any components added to it so all you see is the frame.
You then attempt to add components to your class the is a frame. However, you then have two problems:
you never make this frame visible and
Swing uses layout managers (you don't need to use setBounds(...)). By default it is using the BorderLayout. When you add components to the frame without specifying a constraint the components get added to the "CENTER". However, only one component can be displayed in the "CENTER" so only the last one added will ever be visible.
You also have other problems because you don't create the GUI on the Event Dispatch Thread. So there are really too many problems to correct.
I suggest you read the section from the Swing tutorial on How to Use BorderLayout for a working example of how to create a simple GUI. Then modify that code.
Your JFrame f=new JFrame("Hello!"); is not needed.
You need to use this which is already your JFrame like:
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.setSize(200,200);
this.setTitle("GUI");
Also remove: add(f); and f.setBounds(10, 10, 10, 10);
Because you already extend JFrame, you don't have to create a new JFrame.
Because now your class is a JFrame itself. That means that you can replace every usage of your f-JFrame by using this instead:
That way, also your other Components will be added correctly. Because at the moment you add b, f, i and c to the right JFrame.
So use this:
this.setVisible(true);
this.setSize(200,200);
this.setTitle("GUI");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
or even more simple:
setVisible(true);
setSize(200,200);
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Different classes for different JPanels

I am a Beginner in Java. I have looked all over the place for an answer to my question, but could not find any, or they were to complex for me at this stage of my Java knowledge.
What Planning to do is to create a program on the way of my learning, it will be quite a bit of code and would like to keep things organized.
I have a class where I want to store the mainPanel, where then I would like to add other JPanels which are stored in other classes (on button click (not with a cardLayout)).
My code is probably full of errors, I hope you can help me.
Here is my main class:
import javax.swing.JFrame;
public class TestTool {
public static void main(String[] args){
Test1 frame = new Test1();
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Then I have another class where I want to store the mainPanel and load other panels from different classes:
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test1 extends JFrame {
public Test1(){
testPanel();
}
public void testPanel(){
setTitle("Test Tool");
TestPanel1 te = new TestPanel1();
JPanel mainPanel = new JPanel();
mainPanel.add(te.pan());
}
}
On the next class I have a different panel which I am then trying to load into the mainPanel:
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TestPanel1 {
private JPanel panel1;
public TestPanel1(){
JPanel panel1 = new JPanel();
JLabel label1 = new JLabel("panel 1");
panel1.add(label1);
JButton button1 = new JButton("button 1");
panel1.add(button1);
}
public JPanel pan(){
return panel1;
}
}
Is there anyway I can do it like this? or am I doing it completely wrong?
Thank you
I would suggest making the TestPanel class extend the JPanel class and in your constructor call the superconstructor and then do what you need.

JTextField is blanking out my JPanels

I am attempting to learn more about creating more dynamic GUI's. I am hoping to add different panels with different content and as you press buttons on one main panel, it changes the adjacent panels. I have added two panels and some buttons and when I test the program, it displays correctly. The problem is when I add a JTextField (or JTextArea) the panels are blank and there are no buttons. The strange thing is I haven't added the JTextField to either panel. I have only created a global variable. If I comment it out, the program runs correctly. Am I missing something very simple?
Here is the gameWindow class that has the JTextField
package rpgcreator;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
class gameWindow extends JPanel {
JPanel startWindowPanel;
JPanel settingsPanel;
JPanel characterPanel;
JPanel scenarioPanel;
JPanel mapPanel;
JButton CharacterButton = new JButton("Create your character");
JButton StoryButton = new JButton("Choose your Story line");
JButton MapButton = new JButton("Choose your World");
//JTextField nameField = new JTextField(15); //comment or uncomment to see issue
public gameWindow() {
setLayout(new GridLayout(0,2,5,0));
startWindowPanel = new JPanel(new FlowLayout());
settingsPanel = new JPanel(new GridLayout(2,1));
startWindowPanel.setBackground(Color.blue);
settingsPanel.setBackground(Color.black);
startWindowPanel.add(MapButton);
startWindowPanel.add(StoryButton);
startWindowPanel.add(CharacterButton);
add(startWindowPanel);
add(settingsPanel);
}
}
Here is main
package rpgcreator;
import javax.swing.JFrame;
public class RPGCreator extends JFrame{
private static void mainWindow(){
RPGCreator mainwindow = new RPGCreator();
mainwindow.setSize(1200, 800);
mainwindow.setResizable(false);
mainwindow.setLocationRelativeTo(null);
mainwindow.setTitle("RPG Creator");
mainwindow.setVisible(true);
mainwindow.add(new gameWindow());
mainwindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO code application logic here
mainWindow();
}
}
setVisible should go at the end. You're currently setting visible to true, and then adding a panel.
mainwindow.setVisible(true);
mainwindow.add(new gameWindow());
Put setVisible at the end after setDeaultCLoseOperation
I'm not entirely sure why it does it, maybe someone else can explain.
What I do know, is I usually call pack() which seems to make your problem go away.
private static void mainWindow(){
final RPGCreator mainwindow = new RPGCreator();
mainwindow.setMinimumSize(new Dimension(1200, 800));
mainwindow.setResizable(false);
mainwindow.setTitle("RPG Creator");
mainwindow.setVisible(true);
mainwindow.add(new gameWindow());
mainwindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainwindow.pack(); //This usually goes after you've added all of your components
mainwindow.setLocationRelativeTo(null);
}
Some notes:
I had to change to mainwindow.setMinimumSize(new Dimension(1200, 800)); to avoid the frame looking squashed. Although I would usually let the layout manager deal with the sizes of things.
Call setLocationRelativeTo(null) after you call pack() so that it has the desired effect. Again not sure why, but I've learnt that through some hardship.

Categories