How to switch JPanel with buttons on each individual panel? - java

I'm new to Swing and I'm trying to figure something out. I'm having difficulty explaining what I'm trying to do and I can't find anything online so I wrote a program demonstrating what I want to have happen. I use a CardLayout here:
import java.awt.event.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;
public class Test extends JFrame implements ActionListener {
private CardLayout crd;
private int currentCard;
// Buttons
private JButton twoToCardOne;
private JButton threeToCardOne;
private JButton toCardTwo;
private JButton toCardThree;
// Container
private Container cPane;
// Panels
private JPanel cardOne;
private JPanel cardTwo;
private JPanel cardThree;
public Test() {
currentCard = 1;
cPane = getContentPane();
crd = new CardLayout();
cPane.setLayout(crd);
// Create buttons
twoToCardOne = new JButton("To Card One");
threeToCardOne = new JButton("To Card One");
toCardTwo = new JButton("To Card Two");
toCardThree = new JButton("To Card Three");
twoToCardOne.addActionListener(this);
threeToCardOne.addActionListener(this);
toCardTwo.addActionListener(this);
toCardThree.addActionListener(this);
// Create Panel (Card One)
cardOne = new JPanel();
cardOne.add(toCardTwo);
cardOne.add(toCardThree);
// Create Panel (Card Two)
cardTwo = new JPanel();
cardTwo.add(twoToCardOne);
// Create Panel (Card Three)
cardThree = new JPanel();
cardThree.add(threeToCardOne);
// Add Panels
cPane.add(cardOne);
cPane.add(cardTwo);
cPane.add(cardThree);
}
#Override
public void actionPerformed(ActionEvent e){
int target = 0;
if (e.getSource() == toCardThree){target = 3;}
else if (e.getSource() == toCardTwo){target = 2;}
else {target = 1;}
while (currentCard != target){
crd.next(cPane);
if (currentCard < 3){++currentCard;}
else {currentCard = 1;}
}
}
public static void main(String[] args){
Test test = new Test();
test.setSize(800, 500);
test.setVisible(true);
test.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
So, what I have here are buttons on various panels that all connect to the CardLayout. So, there are different buttons on the different panels that will move me panels when clicked on.
I have two questions/worries here:
If I wanted to make each JPanel its own class, how would I make this work? Since the buttons will be in a different class, I would no longer be able to control the CardLayout with actionPerformed.
My plan is to make this a hockey program where clicking on a button will take you to the player's profile with their stats and my plan was to have each individual player be a panel. This means that I will have A LOT of panels and buttons and I was wondering if there would be a better way to go about doing this. The only thing I've found online is to use CardLayout but this seems like it would not be a very optimal way of doing this.
Any help is much appreciated!
Note: Again, I'm very new to Swing so apologies if what I'm trying to explain is confusing or if I got something completely wrong.

Related

Add Panel with GridLayout onto Background Image

I wanted to create a simple TicTacToe game in java by myself.
But currently I´m struggling with the first issue..
The GUI only displays the Gamefield itself (the "grid") OR the invisble buttons (when you click on them, it displays a X or O)
But never both of them?
I need the gamefield Image as the background and on that the buttons.. But I cant get it working.. I already tried using a third JPanel as the "main" panel with a FlowLayout and added the two other panels onto it.
I created some simple icons and a backround grid in paint.. icons are 200x200 and the grid 600x600 (same as my frame size)
"X-icon" : https://i.stack.imgur.com/tGlZM.png
"O-icon" : https://i.stack.imgur.com/mnoQd.png
"Background" : https://i.stack.imgur.com/BF5yW.png
I Hope anyone can help me out :)
import java.util.ArrayList;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToe implements ActionListener{
ArrayList<JButton> buttons = new ArrayList<JButton>();
JFrame frame;
JPanel gamefield;
JPanel background;
JLabel backgroundLabel;
ImageIcon backgroundImage = new ImageIcon("C:\\Users\\adnan\\Programmieren\\Games\\TicTacToe\\field.png");
ImageIcon iconX = new ImageIcon("C:\\Users\\adnan\\Programmieren\\Games\\TicTacToe\\iconX.png");
ImageIcon iconO = new ImageIcon("C:\\Users\\adnan\\Programmieren\\Games\\TicTacToe\\iconO.png");
int player = 1;
void createGameField(){
background = new JPanel();
background.setPreferredSize(frame.getSize());
backgroundLabel = new JLabel(backgroundImage);
backgroundLabel.setSize(frame.getSize());
background.add(backgroundLabel);
gamefield = new JPanel();
gamefield.setLayout( new GridLayout(3,3,25,25));
createButtons();
frame.add(background);
frame.add(gamefield);
}
void createButtons(){
for(int index = 0; index<=9; index++){
buttons.add(new JButton());
if(index!=0){
buttons.get(index).setOpaque(false);
buttons.get(index).setContentAreaFilled(false);
buttons.get(index).setBorderPainted(false);
gamefield.add(buttons.get(index));
buttons.get(index).addActionListener(this);
}
}
}
public void actionPerformed(ActionEvent e){
if(e.getSource() instanceof JButton){
if(player==1){
System.out.println( ((JButton) e.getSource()).getText() + "was clicked by player "+player);
((JButton) e.getSource()).setEnabled(false);
((JButton) e.getSource()).setIcon(iconX);
player++;
}
else{
System.out.println( ((JButton) e.getSource()).getText() + "was clicked by player "+player);
((JButton) e.getSource()).setEnabled(false);
((JButton) e.getSource()).setIcon(iconO);
player--;
}
}
}
public TicTacToe(){
frame = new JFrame();
createGameField();
frame.setSize(600,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setTitle("TicTacToe");
frame.setResizable(false);
}
public static void main(String[] args) {
TicTacToe newGame = new TicTacToe();
}
}
A FlowLayout will ensure your components will be rendered side by side.
Instead, on the panel that hosts all your UI elements, override paintComponent() so it fills the background with your image. The component will still render it's children (the UI elements) on top of that.
Another chance would be to stack components on top of each other. The lower one would render your image, the upper one the UI - and do not forget to set opaque=false to the UI.

Referencing an external parent JPanel

I am currently trying to code a GUI for an assignment as extra credit, as a learning opportunity. I need to have a menu of buttons, with each button effectively changing the main panel to enter or display data. Right now, I have a class called buttonContainer, which holds the main menu, and mainPanel, which holds the main panel for the entire GUI. Basically I need a way to have buttonContainer add and remove elements from its parent, mainPanel- with the two of them remaining seperate files.
My buttonContainer class looks like this:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class buttonPanel extends JPanel implements ActionListener
{
private JButton load, save, list, find, input, sort, exit;
private JPanel BtnContainer, parent;
private newContactPanel newContact;
public buttonPanel()
{
load = new JButton("Load Contacts");
save = new JButton("Save Contacts");
list = new JButton("List Contacts");
find = new JButton("Find Contact");
sort = new JButton("Sort Contacts");
input = new JButton("New Contact");
exit = new JButton("Exit Program");
newContact = new newContactPanel();
parent = this.getParent();
BtnContainer = new JPanel();
BtnContainer.setLayout(new GridLayout(0,1));
BtnContainer.add(load);
BtnContainer.add(save);
BtnContainer.add(list);
BtnContainer.add(sort);
BtnContainer.add(find);
BtnContainer.add(input);
BtnContainer.add(exit);
add(BtnContainer);
}
#Override
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == input)
{
//Change panel command here
}
}
}
With the mainPanel code looking like this:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class mainPanel extends JPanel //implements ActionListener
{
private buttonPanel MainMenu;
private newContactPanel newContact;
private JPanel wrapper;
public mainPanel()
{
wrapper = new JPanel();
this.setLayout(new BorderLayout());
wrapper.setLayout(new BorderLayout());
MainMenu = new buttonPanel();
newContact = new newContactPanel();
wrapper.add(MainMenu, BorderLayout.WEST);
add(wrapper, BorderLayout.WEST);
}
}
contactPanel is a different panel, that I want buttonPanel to trigger mainPanel to show. Is there any easy way of refrencing the parent class like this, while keeping the two classes separate?
I know variants of this question have been asked before, but, nothing I read here seemed to get done what I wanted. Several of them didn't really match exactly what I was looking for- most of them were from the same file. several used a getParent()- but if I try to use it, it only grabs a Container, and not a JPanel.
EDIT: Thanks to the people who answered. A couple of good ideas were presented- which helped me realize I forgot to actually add the action listener to the button. This question has been fully solved!
Basically I need a way to have buttonContainer add and remove elements from its parent, mainPanel- with the two of them remaining
seperate files.
You can use SwingUtilities.getAncestorOfClass(class, comp) from within your ButtonContainer class as you know it will be a child of that class. For example:
class ButtonContainer extends JPanel implements ActionListener {
...
#Override
public void actionPerformed(ActionEvent e) {
MainPanel mainPanel = (MainPanel)SwingUtilities.getAncestorOfClass(MainPanel.class, ButtonContainer.this);
if (mainPanel != null) {
// Change panel command here
}
}
}
Off-topic
Please read Java Code Conventions and stick to them. Class' names start with a capital letter; variables and methods names start with lower case.
you can make wrapper public static
public static JPanel wrapper;
then from buttonPanel you can use this code
mainPanel.wrapper.remove(<whatever component>);
mainPanel.wrapper.add(<whatever component>);
mainPanel.wrapper.validate();
mainPanel.wrapper.repaint();
i think this will work

Makes two JPanels when I only need one

package Rectangle;
import java.awt.*;
import javax.swing.*;
public class Rectangle extends JFrame {
public Rectangle(String arg) {
JPanel panel = new JPanel(); enter code here
panel.setBackground(Color.BLACK);
ImageIcon icon = new ImageIcon(this.getClass().getResource("1676858-livingforest2011.jpg"));
JLabel label = new JLabel();
label.setIcon(icon);
panel.add(label);
this.getContentPane().add(panel);
}
public static void main(String[] args) {
Rectangle forestFrame = new Rectangle(args.length == 0 ? null : args[0]);
forestFrame.setSize(1698,770);
forestFrame.setVisible(true);
new Rectangle("/Users/computerscience2/Desktop/2njk8eq.png").setVisible(true);
}
}
It prints out two Jpanels, one that I want and one that I don't. It also prints out the one that I want the size that I want and the second one is the smallest it can be. How do I get rid of the second Jpanel?
You create 2 Rectangle objects via new operator, which creates 2 JPanel instances.
Abandon creating one of them.

Simple 8 rooks game for beginner

Hi I am new to java and I thought I'd try produce a game where the user actually tries solving the 8 queens problem themselves. However, It increases in difficulty starting 8 rooks, up to 14 bishops then 8 queens.
I have created the chessboard successfully. I have a problem with my mouselistener... each square on the board is a button and when clicked my intention is that that square will change colour to indicate its been clicked, then all squares that cant be clicked on again will also change to indicate squares out of the game.
When the square is clicked it doesn't seem to perform any action.
Sorry, I know its trivial.
Thanks.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class rooks extends JFrame implements MouseListener{
private final int BOARD_SIZE = 8;
private final int BOARD_SIZE_COLS = 8;
private final int BOARD_SIZE_ROWS = 8;
// private JTextField bottom = new JTextField("") ");
// private JLabel bannerl = new JLabel("The game");
// private JButton queens = new JButton(" Play Queens ");
private JButton rooks = new JButton(" Play Rooks ");
// private JButton bishops = new JButton(" Play Knights ");
private JButton[][] cboard = new JButton[BOARD_SIZE][BOARD_SIZE];
private JTextArea bottomtextarea = new JTextArea();
// constructor creating the chessboard
public rooks(){
this.setSize(500, 500);
this.setTitle("rooks");
// this.setIconImage();
// create JPanels and add JComponents
JPanel main = new JPanel(new BorderLayout());
this.setContentPane(main);
JPanel north = new JPanel();
north.setLayout(new GridLayout(1,3));
main.add(north, BorderLayout.NORTH);
// north.add(queens);
north.add(rooks);
// north.add(bishops);
JPanel south = new JPanel();
main.add(south, BorderLayout.SOUTH);
south.add(bottomtextarea);
bottomtextarea.setEditable(false);
bottomtextarea.setVisible(true);
// create grid (actual chessboard) and initialise each button with no char
JPanel chessBoard = new JPanel(new GridLayout(BOARD_SIZE, BOARD_SIZE));
main.add(chessBoard, BorderLayout.CENTER);
for (int i=0; i<BOARD_SIZE_ROWS; i++){
for(int j=0; j<BOARD_SIZE_COLS; j++){
cboard[i][j] = new JButton("");
chessBoard.add(cboard[i][j]);
// as it loops add colour to the board, if (i+j=even then white, otherwise black)
if ((i + j) % 2 == 0) {
cboard[i][j].setBackground(Color.black);
}
else {
cboard[i][j].setBackground(Color.white);
}
}
}
cboard[7][7].addMouseListener(this);
this.setResizable(false);
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void mousePressed(MouseEvent e){
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
System.out.print("it has been clicked");
}
void saySomething(String eventDescription, MouseEvent e) {
}
}
Your code is working. I run it and when i click on the 7-7 square, (which is the one in the bottom right corner) i get the message: "it has been clicked".
Since you have added the mouse listener to only this square the code is behaving as expected.
But there are some things you should refactor:
Why do you define BOARD_SIZE, BOARD_SIZE_COLS, BOARD_SIZE_ROWS? If you only use quadratic game boards you only need BOARD_SIZE and if not then you dont need BOARD_SIZE.
Its convention to write the first letter of your classes in upper case. So it is Rooks instead of rooks
You need to add your listener to every square of the board instead of to only one
This should be enough to start with.
You are adding a MouseListener to the last button only, the JButton at cboard[7][7].
Why use a MouseListener and not an ActionListener for JButtons? This makes no sense.
Why not add an ActionListener to all JButtons inside of the for loop?

About java GUI and clear JFrame to show new content?

I'm learning Java and GUI. I have some questions, and the first is if there are any major difference between creating a subclass of JFrame and an instance of JFrame. It seems like like a subclass is more powerful? I also wonder if it's necessary to use this code when creating a GUI:
Container contentPane = getContentPane();
contentPane.setLayot(new Flowlayout());
I add my GUI class, it's a simple test so far, to a task that I have to hand in. When a user has entered some text in the textfield and press the button to continue to the next step, how do I do to clear the frame and show a new content or is there a special way to do this is in Java? I guess there must be better to use the same window instead of creating a new!? Help id preciated! Thanks
// Gui class
import java.awt.FlowLayout; // layout
import java.awt.event.ActionListener; // listener
import java.awt.event.ActionEvent; // event
import javax.swing.JFrame; // windows properties
import javax.swing.JLabel; // row of text
import javax.swing.JTextField; // enter text
import javax.swing.JOptionPane; // pop up dialog
import javax.swing.JButton; // buttons
// import.javax.swing.*;
public class Gui extends JFrame {
private JLabel text1;
private JTextField textInput1;
private JTextField textInput2;
private JButton nextButton;
// constructor creates the window and it's components
public Gui() {
super("Bank"); // title
setLayout(new FlowLayout()); // set default layout
text1 = new JLabel("New customer");
add(text1);
textInput1 = new JTextField(10);
add(textInput1);
nextButton = new JButton("Continue");
add(nextButton);
// create object to handle the components (action listener object)
frameHandler handler = new frameHandler();
textInput1.addActionListener(handler);
nextButton.addActionListener(handler);
}
// handle the events (class inside another class inherits contents from class outside)
private class frameHandler implements ActionListener {
public void actionPerformed(ActionEvent event){
String input1 = "";
// check if someone hits enter at first textfield
if(event.getSource() == textInput1){
input1 = String.format(event.getActionCommand());
JOptionPane.showMessageDialog(null, input1);
}
else if(event.getSource() == nextButton){
// ??
}
}
}
}
This small code might help you explain things :
import java.awt.event.*;
import javax.swing.*;
public class FrameDisplayTest implements ActionListener
{
/*
* Creating an object of JFrame instead of extending it
* has no side effects.
*/
private JFrame frame;
private JPanel panel, panel1;
private JTextField tfield;
private JButton nextButton, backButton;
public FrameDisplayTest()
{
frame = new JFrame("Frame Display Test");
// If you running your program from cmd, this line lets it comes
// out of cmd when you click the top-right RED Button.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
panel1 = new JPanel();
tfield = new JTextField(10);
nextButton = new JButton("NEXT");
backButton = new JButton("BACK");
nextButton.addActionListener(this);
backButton.addActionListener(this);
panel.add(tfield);
panel.add(nextButton);
panel1.add(backButton);
frame.setContentPane(panel);
frame.setSize(220, 220);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent ae)
{
JButton button = (JButton) ae.getSource();
if (tfield.getText().length() > 0)
{
if (button == nextButton)
{
/*
* this will remove the first panel
* and add the new panel to the frame.
*/
frame.remove(panel);
frame.setContentPane(panel1);
}
else if (button == backButton)
{
frame.remove(panel1);
frame.setContentPane(panel);
}
frame.validate();
frame.repaint(); // prefer to write this always.
}
}
public static void main(String[] args)
{
/*
* This is the most important part ofyour GUI app, never forget
* to schedule a job for your event dispatcher thread :
* by calling the function, method or constructor, responsible
* for creating and displaying your GUI.
*/
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new FrameDisplayTest();
}
});
}
}
if you want to switch (add then remove) JComponents, then you have to
1) add/remove JComponents and then call
revalidate();
repaint()// sometimes required
2) better and easiest choice would be implements CardLayout
If your requirement is to make a wizard, a panel with next and prev buttons, and on clicking next/prev button showing some component. You could try using CardLayout.
The CardLayout manages two or more components (usually JPanel instances) that share the same display space. CardLayout let the user choose between the components.
How to Use CardLayout
If your class extends JFrame, you can do:
getContentPane().removeAll();

Categories