CardLayout in Java change by action in one of the 'cards' - java

I am making a simple game using a JFrame. I have made a simple "Start" screen which basically consists of a String and a JButton. I am picking up the button click with the actionPerformed(ActionEvent e) method. I don't know how to change the cards using a button click. This may seem like a simple problem to solve, but the twist comes with this: My main JFrame, my StartScreen and my JPanel where the game takes place are all in separate files. My main file, Virus.java, is where I create the JFrame. My file VirusGamePanel.java is where the game takes place. My file StartScreen.java is the screen with the button. I want to change 'cards' to the game screen when the player clicks the button. How can I do this?
My StartScreen.java file:
package virus;
import javax.swing.JPanel;
import java.awt.event.ActionListener;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import javax.swing.JButton;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.CardLayout;
public class StartScreen extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
JButton start = new JButton("Start");
public StartScreen(){
start.addActionListener(this);
start.setBounds(new Rectangle(400,300,100,30));
this.add(start);
}
public void paint(Graphics g){
super.paint(g);
g.setFont(new Font("Impact",Font.BOLD,72));
g.setColor(Color.MAGENTA);
g.drawString("Virus",275,300);
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==start)
{
//what to do here?
}
}
}
My Virus.java file:
package virus;
import javax.swing.*;
import java.awt.CardLayout;
import virus.StartScreen;
public class Virus extends JFrame{
private static final long serialVersionUID =1L;
JFrame jf = new JFrame("Virus");
static JPanel thegame = new JPanel(new CardLayout());
JPanel game = new VirusGamePanel();
JPanel start = new StartScreen();
public Virus(){
jf.setResizable(false);
jf.setSize(600,600);
jf.setLocationRelativeTo(null);
jf.setDefaultCloseOperation(EXIT_ON_CLOSE);
jf.setVisible(true);
jf.add(thegame);
thegame.add(start);
thegame.add(game);
}
public static void main(String[] args) {
new Virus();
}
}

You simply have to right this in your actionPerformed(...) method :
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==start)
{
//what to do here?
CardLayout cardLayout = (CardLayout) Virus.thegame.getLayout();
cardLayout.next(Virus.thegame);
}
}
As very much pointed out by #kleopatra (THE EMPRESS) herself, don't override paint() instead do your painting stuff inside paintComponent(Graphics g) method of any JPanel/JComponent. Moreover, first add the components to your JFrame, once it's size is realized, then only set it to Visible, not before that. Instead of setting sizes for the JFrame simply override the JPanel's method getPreferredSize(), make it return some valid Dimension Object.
Do watch this sequence, as you write your code the next time :
public Virus(){
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setResizable(false);
thegame.add(start);
thegame.add(game);
jf.add(thegame);
jf.pack();
jf.setLocationRelativeTo(null);
jf.setVisible(true);
}
Here is your full code :
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.ActionListener;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.CardLayout;
public class Virus extends JFrame{
private static final long serialVersionUID =1L;
JFrame jf = new JFrame("Virus");
static JPanel thegame = new JPanel(new CardLayout());
JPanel game = new VirusGamePanel();
JPanel start = new StartScreen();
public Virus(){
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setResizable(false);
thegame.add(start);
thegame.add(game);
jf.add(thegame);
jf.pack();
jf.setLocationRelativeTo(null);
jf.setVisible(true);
}
public static void main(String[] args) {
new Virus();
}
}
class StartScreen extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
JButton start = new JButton("Start");
public StartScreen(){
start.addActionListener(this);
start.setBounds(new Rectangle(400,300,100,30));
this.add(start);
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setFont(new Font("Impact",Font.BOLD,72));
g.setColor(Color.MAGENTA);
g.drawString("Virus",275,300);
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(600, 600));
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==start)
{
//what to do here?
CardLayout cardLayout = (CardLayout) Virus.thegame.getLayout();
cardLayout.next(Virus.thegame);
}
}
}
class VirusGamePanel extends JPanel
{
public VirusGamePanel()
{
JLabel label = new JLabel("I am ON", JLabel.CENTER);
add(label);
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(600, 600));
}
}

Your StartScreen class has to have access to the instance of the CardLayout of the JFrame and the instance of the VirusGamePanel class. You can pass these instances in the constructor or a setLayout method and setVirusGamePanel method of your StartScreen class.
Something like:
layout.next(virusGamePanel);
should work.

Related

How to properly call a shape drawing method in an ActionListener

I'm trying to make it so that after clicking on the button, a figure is added to the canvas.
But I can't correctly implement the listener method in the OvalDrawTool class.
package com.company;
import javax.swing.*;
import java.awt.*;
public class TestFrame extends JFrame{
public final OvalDrawTool odt = new OvalDrawTool();
public static void main(String[] args){
new TestFrame().start();
}
public void start(){
setLayout(new BorderLayout());
JPanel buttonsPanel = new JPanel();
JButton buttonOval = odt.getButtonOval();
buttonsPanel.add(buttonOval);
add(buttonsPanel, BorderLayout.NORTH);
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
I do not understand how should I create a Graphics object and pass it in actionPerformed method
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class OvalDrawTool implements ActionListener{
public JButton getButtonOval() {
JButton buttonOval = new JButton();
buttonOval.setPreferredSize(new Dimension(100, 50));
buttonOval.setText("Oval");
buttonOval.addActionListener(this);
return buttonOval;
}
public void paintOval(Graphics g) {
g.setColor(Color.green);
g.fillOval(100, 100, 50, 50);
}
#Override
public void actionPerformed(ActionEvent e) {
paintOval();
}
}
I moved the figure drawing method into a separate class since I want to add more other shapes.

BorderLayout orientation

I'm trying to create the top buttons of a window. I have a JFrame and a JPanel with the differents buttons when I try to add the panel with the buttons to a JPanel on the frame, it doesn't show... digging and trying to find the solution, I realize that the issue is when I set the orientation to the panel with the buttons on the BorderLayout panel. I think that it might be something dumb that I haven't realize but I haven't found any issue like this.
The issue is here when I set the orientation:
contentPanel.add(buttons,BorderLayout.PAGE_START);
if I remove the:
BorderLayout.PAGE_START
it works
This is my Frame:
package view;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.CardLayout;
import java.awt.BorderLayout;
public class MainFrame extends JFrame{
private JPanel contentPanel, layOutPanel;
private CardLayout mainCardLayout;
private BorderLayout borderLayout;
private static MainFrame instance = null;
private FrameButtonsPanel buttons;
private MainFrame(){
setSize(1000,700);
//setUndecorated(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPanel = new JPanel();
borderLayout = new BorderLayout();
contentPanel.setLayout(borderLayout);
add(contentPanel);
buttons = new FrameButtonsPanel();
buttons.setBackground(Color.red);
contentPanel.add(buttons,BorderLayout.PAGE_START);
/*layOutPanel = new JPanel();
mainCardLayout = new CardLayout();
layOutPanel.setLayout(mainCardLayout);
layOutPanel.setBackground(Color.red);
contentPanel.add(layOutPanel,BorderLayout.SOUTH);*/
}
public static MainFrame getInstance(){
if (instance == null){
instance = new MainFrame();
}
return instance;
}
public static void main(String[] args) {
MainFrame.getInstance().setVisible(true);
}
}
and this is my panel with the buttons:
package view;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.Spring;
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class FrameButtonsPanel extends JPanel{
private Spring spring;
private JButton iconify, maximize, close;
public FrameButtonsPanel(){
SpringLayout mySpring = new SpringLayout();
setLayout(mySpring);
iconify = new JButton("-");
add(iconify);
maximize = new JButton("O");
add(maximize);
close = new JButton("X");
add(close);
spring = Spring.constant(850,850,2000);
mySpring.putConstraint(SpringLayout.WEST,iconify,spring,SpringLayout.WEST,this);
mySpring.putConstraint(SpringLayout.WEST,maximize,3,SpringLayout.EAST,iconify);
mySpring.putConstraint(SpringLayout.WEST,close,3,SpringLayout.EAST,maximize);
mySpring.putConstraint(SpringLayout.EAST,this,3,SpringLayout.EAST,close);
iconifyWindow();
maximizeWindow();
closeWindow();
}
private void iconifyWindow(){
iconify.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
MainFrame.getInstance().setExtendedState(JFrame.ICONIFIED);
}
});
}
private void maximizeWindow(){
maximize.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
if(MainFrame.getInstance().getExtendedState() == JFrame.MAXIMIZED_BOTH){
MainFrame.getInstance().setExtendedState(JFrame.NORMAL);
}else{
MainFrame.getInstance().setExtendedState(JFrame.MAXIMIZED_BOTH);
}
}
});
}
private void closeWindow(){
close.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
}
}
I have no idea why you are trying to use a SpringLayout to display buttons.
Just use a JPanel with a right aligned FlowLayout.
Read the FlowLayout API for more information on how to right align the components added to the panel.

Creating a class that extends Jlabel to draw a string to a panel. Nothing is currently being displayed

I am new to the swing and graphics and am trying to get this to work for drawing a string on a label by making an object class called DrawString. A panel currently pops up with nothing on it. I would like to thank you for any guidance you can give.
package view;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Run {
public static void main(String[] args) {
JPanel panel = new JPanel();
DrawString text = new DrawString();
JFrame window = new JFrame();
window.setVisible(true);
window.setSize(400, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
text.display("Boo");
panel.setLocation(((window.getWidth()/2)-(panel.getWidth()/2)),
((window.getHeight()/2)-(panel.getHeight()/2)));
panel.setSize(200, 200);
window.add(panel);
panel.add(text);
}
}
package view;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JLabel;
#SuppressWarnings("serial")
public class DrawString extends JLabel{
String string;
Font font = new Font("TimesRoman",Font.PLAIN, 30);
public DrawString() {
super();
}
public void display(String string){
this.string=string;
repaint();
}
public void drawString(Graphics comp){
super.paintComponent(comp);
Graphics2D g = (Graphics2D) comp;
g.setFont(font);
g.drawString(string, JLabel.CENTER, JLabel.CENTER);
}
}
I am learning and experimenting…
It's not clear if you want to experiment with JLabel or custom painting. You might want to start with a working JLabel#setText() example or TextLayout#draw() example.

How to switch between JPanels in my JFrame

I don't know why this does not work. I have implemented an ItemListener to check if the continue button is clicked on on the SplashScreen to then switch to the MainMenu panel. When I run the code, I get the first panel with the background image and button and then when I click the button nothing happens.
Here is my Window class
package edu.ycp.cs.Main;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Container;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JButton;
import javax.swing.JPanel;
public class Window implements ItemListener{
private static final long serialVersionUID = 1L;
JPanel cards; // a panel that uses CardLayout
final static String SPLASHSCREEN = "SplashScreen";
final static String MAINMENU = "MainMenu";
public void addComponentToWindow(Container pane) {
// Put the JComboBox in a JPanel to get a nicer look.
JPanel gameWindow = new JPanel(); // use FlowLayout
// Create the "cards".
JPanel card1 = new JPanel();
JButton continueButton = new JButton("Continue");
continueButton.addItemListener(this);
card1.add(new SplashScreen());
card1.add(continueButton);
JPanel card2 = new JPanel();
card2.add(new MainMenuScreen());
cards = new JPanel(new CardLayout());
cards.add(card1, SPLASHSCREEN);
cards.add(card2, MAINMENU);
pane.add(gameWindow, BorderLayout.PAGE_START);
pane.add(cards, BorderLayout.CENTER);
}
public void itemStateChanged(ItemEvent evt) {
CardLayout cl = (CardLayout) (cards.getLayout());
cl.show(cards, (String) evt.getItem());
}
}
Here is my splashscreen code
the MainMenu code is exactly the same with a different image file
package edu.ycp.cs.Main;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
class SplashScreen extends JPanel {
private static final long serialVersionUID = 1L;
private BufferedImage background;
public SplashScreen() {
try {
background = ImageIO.read(new File("Logo.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
int x = (getWidth() - background.getWidth());
int y = (getHeight() - background.getHeight());
g.drawImage(background, x, y, this);
}
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(Color.white);
g2d.drawString("Please wait...", getWidth() / 2, getHeight() * 3 / 4);
}
}
and here is my Main
package edu.ycp.cs.Main;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
makeGUI();
}
});
}
private static void makeGUI() {
// Create and set up the window.
JFrame frame = new JFrame("M&M Arcade");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
Window window = new Window();
window.addComponentToWindow(frame.getContentPane());
// Display the window.
frame.pack();
frame.setVisible(true);
}
}
An ItemListener will have no effect when you click on the continue JButton. Use an ActionListener instead:
continueButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout) (cards.getLayout());
cl.show(cards, MAINMENU);
}
});
Consider also using CardLayout#next for navigation.

Interchangeable JComponent skins for JButton

I'm attempting to create a custom JButton that has interchangeable skin components. Using CardLayout as the switching mechanism, I'm having difficulty with the JComponent (i.e. skin component) laying flush across the JButton.
For instance,
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public final class SkinsDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new JSkinnableButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static final class JSkinnableButton extends JButton{
private static final long serialVersionUID = -5167346969674067012L;
protected JSkinnableButton(){
super();
setLayout(new CardLayout()); // for interchangeability
add(new JSkinComponent(), "Skin");
}
}
private static final class JSkinComponent extends JComponent{
private static final long serialVersionUID = 2172542865655802012L;
protected JSkinComponent(){
super();
setOpaque(true);
setLayout(new FlowLayout()); // need layout manager
setBackground(Color.CYAN);
add(new JLabel("Skin"));
}
#Override
protected void paintComponent(Graphics g){
Graphics gCopy = g.create();
gCopy.setColor(getBackground());
gCopy.fillRect(0, 0, getWidth(), getHeight());
gCopy.dispose();
}
}
}
That's a really crude example, but I think it conveys my intentions clearly.
And this JButton will be listening for property change events from a domain object and will update it's display accordingly.
There's space being taken up in your JSkinnableButton by the button's margin and border.
protected JSkinnableButton(){
super();
setLayout(new CardLayout()); // for interchangeability
setMargin(new Insets(0,0,0,0));
setBorder(BorderFactory.createEmptyBorder());
add(new JSkinComponent(), "Skin");
}
Now, the border in particular, is a part of what makes a button look like a button, but I assume you've already got a plan for that...

Categories