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...
Related
basically I'm trying to understand Threads in Java.So I thought I'd create a main JFrame class containing two JPanels from external classes and then do something in one and control it with messages from the second panel.So far I have only created the first external panel and there the probleme starts! It does not show correctly although it appears to be "loaded".(see system.out lines)
So here is the Main Class
package com.maybee.gui;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class Maybee extends JFrame implements Runnable
{
public JFrame maynFrame = null;
public JPanel contentPanel = null;
public SimPanel simPanel = null;
public int screenWidth = 0;
public int screenHeight = 0;
public Maybee()
{
}
private void init()
{
System.out.println("In Inint");
maynFrame = new JFrame("Maybee");
maynFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
screenWidth = gd.getDisplayMode().getWidth();
screenHeight = gd.getDisplayMode().getHeight();
maynFrame.setPreferredSize(new Dimension(screenWidth,screenHeight - 100));
maynFrame.setContentPane(getContentPanel());
maynFrame.setVisible(true);
maynFrame.pack();
}
public JPanel getContentPanel()
{
if (contentPanel == null)
{
contentPanel = new JPanel();
contentPanel.setPreferredSize(new Dimension(screenWidth,screenHeight - 100));
contentPanel.setBorder(new LineBorder(Color.BLUE));
contentPanel.setBackground(Color.RED);
contentPanel.setLayout(new BorderLayout());
contentPanel.add(getSimPanel(),BorderLayout.CENTER);
}
return contentPanel;
}
public SimPanel getSimPanel()
{
if(simPanel == null)
{
simPanel = new SimPanel(this);
}
return simPanel;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
System.out.println("Start");
Maybee maybee = new Maybee();
maybee.run();
}
});
}
public void run()
{
init();
}
}
and now the first external JPanel class
package com.maybee.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
public class SimPanel extends JPanel
{
public Maybee localMaybee = null;
public JPanel simPanel = null;
private JButton btn;
public SimPanel(Maybee interMaybee)
{
localMaybee = interMaybee;
init();
}
public void init()
{
simPanel = new JPanel();
simPanel.setLayout(new BorderLayout());
simPanel.setPreferredSize(new Dimension(localMaybee.screenWidth/4,localMaybee.screenHeight - 100));
simPanel.setBackground(Color.GREEN);
simPanel.add(getBtn(),BorderLayout.CENTER);
simPanel.setVisible(true);
System.out.println("IN SIM" + localMaybee.screenWidth);
}
public JButton getBtn()
{
if(btn == null)
{
btn = new JButton("ENDE");
btn.setSize(70, 20);
btn.setForeground(Color.YELLOW);
btn.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
}
});
}
return btn;
}
}
So what am I missing?
Many thanks!
The immediate issue is the second instance of JPanel created in SimPanel.init(). SimPanel is already a JPanel, there is no need to maintain public JPanel simPanel member.
The same problem is in the Maybee class which extends JFrame, but maintains public JFrame maynFrame member.
Also, as already mentioned in comments above (thanks #Frakcool!) :
Make sure to call pack() before setVisible();
Don't call setPreferredSize(), do override getPreferredSize() intead;
No need to extend JFrame;
No need to call setVisible on JPanel;
Don't call btn.setSize(), it is a job for a layout manager;
No need for setContentPane(), JFrame by default has a JPanel as content pane with BorderLayout. Calling add() is enough in this case.
Here is a slightly modified version of the original code (simplified for clarity):
import java.awt.BorderLayout;
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;
import javax.swing.SwingUtilities;
public class Maybee2 {
static class SimPanel extends JPanel {
public SimPanel() {
setLayout(new BorderLayout());
JButton btn = new JButton("ENDE");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//TODO
}
});
add(btn, BorderLayout.CENTER);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
}
private static void createAndShowGUI() {
final JFrame frame = new JFrame("Maybee");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SimPanel simPanel = new SimPanel();
frame.add(simPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
EDIT:
The application may consist of many panels. The high level container such as JFrame is not aware of all the underlying layout complexities and cannot enforce a certain size. The panel itself knows its internal layout and its content. So the panel reports its preferred size to the layout manager which eventually packs the content. See Laying Out Components Within a Container for more information.
setBackground has its effect although the button occupies the center of the BorderLayout which takes all the space of the panel. Change the layout of the panel and see the effect. Or move the button into another area, ie - add(btn, BorderLayout.NORTH); Read more in A Visual Guide to Layout Managers.
I have a Jframe with two buttons: '1' and '2'. Clicking the button '1' should display the capital letter A in the JPanel.
Code fore my JFrame:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawFrame extends JFrame{
private final int WIDTH = 500;
private final int HEIGHT = 300;
private JButton number1;
private JButton number2;
private JPanel numberPanel;
private DrawPanel graphicsPanel;
public DrawFrame()
{
createSelectionPanel();
createGraphicsPanel();
this.setSize(WIDTH, HEIGHT);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
private void createSelectionPanel()
{
numberPanel = new JPanel();
number1 = new JButton("1");
number2 = new JButton("2");
numberPanel.setLayout(new GridLayout(2,2));
numberPanel.add(number1);
numberPanel.add(number2);
this.add(numberPanel, BorderLayout.WEST);
}
private void createGraphicsPanel()
{
//instantiate drawing panel
graphicsPanel = new DrawPanel();
//add drawing panel to right
add(graphicsPanel);
}
private class Number1ButtonListener implements ActionListener {
public void actionPerformed (ActionEvent event) {
Number number = new Number();
number.setNumber('A');
}
}
//creates a drawing frame
public static void main(String[] args)
{
DrawFrame draw = new DrawFrame();
}
}
Code for my JPanel
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class DrawPanel extends JPanel{
public Coordinates current;
public DrawPanel(){
//nothing drawn initially
current = null;
//set white background for drawing panel
setBackground(Color.WHITE);
//add mouse listeners
MouseHandler mouseHandler = new MouseHandler();
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
public void paint(Graphics g){
super.paint(g);
if(current!=null){
I want to replace "A" with number.getNumber()
g.drawString("A", current.getX(), current.getY());
}
}
//class to handle all mouse events
private class MouseHandler extends MouseAdapter implements MouseMotionListener
{
public void mousePressed(MouseEvent event)
{
current = new Coordinates(event.getX(), event.getY());
}
public void mouseReleased(MouseEvent event)
{
repaint();
}
}
}
I'm not sure if this is possible. So sorry if I am mistaken in my logic. Please provide an alternate way for me to approach this problem. Appreciate any guidance.
Thanks!
The Coordinates and Number classes weren't included, so I had to modify the code somewhat.
Here's the GUI I created.
The first thing I did was create a model class for the GUI. By creating a model class, I could make the display string and the drawing coordinate available to the view and the controller classes. This is a simple example of the model / view / controller pattern.
package com.ggl.drawing;
import java.awt.Point;
public class GUIModel {
private String displayString;
private Point coordinate;
public GUIModel(String displayString) {
this.displayString = displayString;
}
public Point getCoordinate() {
return coordinate;
}
public void setCoordinate(int x, int y) {
this.coordinate = new Point(x, y);
}
public void setCoordinate(Point coordinate) {
this.coordinate = coordinate;
}
public void setDisplayString(String displayString) {
this.displayString = displayString;
}
public String getDisplayString() {
return displayString;
}
}
Now that we have a model, lets look at the DrawFrame class.
package com.ggl.drawing;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawFrame implements Runnable {
private final int WIDTH = 500;
private final int HEIGHT = 300;
private JFrame frame;
private GUIModel model;
public DrawFrame() {
this.model = new GUIModel("A");
}
#Override
public void run() {
frame = new JFrame("Draw Letters");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createSelectionPanel(), BorderLayout.WEST);
frame.add(new DrawPanel(WIDTH, HEIGHT, model), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private JPanel createSelectionPanel() {
JPanel numberPanel = new JPanel();
ButtonListener listener = new ButtonListener();
JButton number1 = new JButton("A");
number1.addActionListener(listener);
JButton number2 = new JButton("B");
number2.addActionListener(listener);
numberPanel.setLayout(new GridLayout(0, 2));
numberPanel.add(number1);
numberPanel.add(number2);
return numberPanel;
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
model.setDisplayString(event.getActionCommand());
}
}
// creates a drawing frame
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawFrame());
}
}
I started the Java Swing application on the Event Dispatch thread with the call to the SwingUtilities invokeLater method.
I separated the JFrame construction from the 2 JPanels construction. I used a JFrame, rather than extend a JFrame. The only time you should extend any Java class is if you want to override one or more of the class methods.
I used the same ButtonListener for both JButtons. I'm guessing what you want, but I drew either an "A" or a "B", depending on which button you left clicked.
Let's look at the DrawPanel class.
package com.ggl.drawing;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
public class DrawPanel extends JPanel {
private static final long serialVersionUID = 3443814601865936618L;
private GUIModel model;
public DrawPanel(int width, int height, GUIModel model) {
this.setPreferredSize(new Dimension(width, height));
this.model = model;
// add mouse listeners
MouseHandler mouseHandler = new MouseHandler();
this.addMouseListener(mouseHandler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (model.getCoordinate() != null) {
Point p = model.getCoordinate();
Font font = g.getFont().deriveFont(48F);
g.setFont(font);
g.drawString(model.getDisplayString(), p.x, p.y);
}
}
// class to handle all mouse events
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent event) {
model.setCoordinate(event.getPoint());
}
#Override
public void mouseReleased(MouseEvent event) {
DrawPanel.this.repaint();
}
}
}
The major change I made in this class was to use the paintComponent method, rather than the paint method. The paintComponent method is the correct method to override.
I set the size of the drawing panel in the DrawPanel constructor. It's much better to let Swing figure out the size of the JFrame. That's what the pack method in the DrawFrame run method does.
I increased the font size so you can see the drawn letter better.
I removed the mouse motion listener code, as it wasn't needed.
I hope this was helpful to you.
OK, all I know so far is that you want the text displayed in a JPanel to change if a button is pressed. If so, then your code looks to be way too complex for the job. Suggestions include:
Give the DrawingPanel a setter method, say, setText(String text), that allows outside classes to change the text that it displays.
Within that method, set a field of DrawingPanel, say called text, and call repaint().
Override DrawingPanel's paintComponent not its paint method, and call the super's method within your override.
Within the paintComponent method, call drawString to draw the String held by the text field, if the field is not null.
Give your buttons ActionListeners or AbstractActions that call the DrawingPanel's setText(...) method, setting the text to be displayed.
For example:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class DrawAorB extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
public DrawAorB() {
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 5));
btnPanel.add(new JButton(new ButtonAction("A")));
btnPanel.add(new JButton(new ButtonAction("B")));
setLayout(new BorderLayout());
add(drawingPanel, BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
String text = e.getActionCommand();
drawingPanel.setText(text);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawAorB");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawAorB());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class DrawingPanel extends JPanel {
private static final int PREF_W = 200;
private static final int PREF_H = PREF_W;
private String text = null;
public void setText(String text) {
this.text = text; // set the JPanel's text
repaint(); // and draw it
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (text != null) {
int x = getWidth() / 2;
int y = getHeight() / 2;
// use FontMetrics if you want to center text better
g.drawString(text, x, y);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
Even simpler, easier, and probably better would be to display the text within a JLabel as it's much easier to center this text.
I've a main frame on which there is a side panel with some buttons, and central panel used to display the tables and data generated from buttons on the side panel and its sub-panels
On the start my central panel is blank and I want it to always return to its initial state( blank ) after each click on a button before generating any data
I've use some sort of observer pattern (I'm not so experienced) but my problem is that the central panel must display data after clicks on some buttons that are on panels that also need a click on the side panel before to be generated
I've tried to make an executable example on the following classes, my real application displays some tables on the central panel and i send the models via the update method of the observers
hope its clear for you and I hope if you can really help me
1 - the main frame:
package tests;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MainFrame extends JFrame implements MyObserver{
private SidePanel sidePanel;
private JPanel centralPanel;
private JFrame frame;
private JLabel title;
public MainFrame(){
frame = new JFrame("TEST");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
sidePanel = new SidePanel();
sidePanel.addObserver(this);
centralPanel = new JPanel();
title = new JLabel();
initialise(0);
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
private void initialise(int i) {
if( i == 0){
centralPanel.setPreferredSize(new Dimension(400,300));
centralPanel.setBackground(Color.green);
title.setText("GREEN");
centralPanel.add(title, BorderLayout.CENTER);
frame.add(sidePanel, BorderLayout.WEST);
frame.add(centralPanel, BorderLayout.CENTER);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MainFrame();
}
});
}
#Override
public void update(int color) {
if(color == 0){
centralPanel.setBackground(Color.yellow);
title.setText("YELLOW");
}else{
centralPanel.setBackground(Color.pink);
title.setText("PINK");
}
}
}
2 - The side Panel
package tests;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
public class SidePanel extends JPanel implements MyObserver,MyObservable{
private JPanel panel;
private JButton test;
private MyObserver observer;
private ButtonPanel buttonPanel;
public SidePanel(){
panel = new JPanel();
panel.setPreferredSize(new Dimension(140, 300));
panel.setBackground(Color.blue);
panel.setLayout(new BoxLayout(panel, 0));
test = new JButton("Lunch buttons");
test.setPreferredSize(new Dimension(80,30));
buttonPanel = new ButtonPanel();
buttonPanel.addObserver(this);
test.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
buttonPanel.setVisible(true);
}
});
panel.add(Box.createVerticalGlue());
panel.add(test);
panel.add(Box.createVerticalGlue());
panel.setVisible(true);
this.add(panel, BorderLayout.CENTER);
}
#Override
public void addObserver(MyObserver obs) {
this.observer = obs;
}
#Override
public void updateObserver(MyObserver obs, int color) {
obs.update(color);
}
#Override
public void update(int color) {
updateObserver(observer, color);
}
}
3 - the buttons panel, generally the source of any data to be displayed on the central panel
package tests;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
public class ButtonPanel extends JDialog implements MyObservable{
private JButton yellow;
private JButton orange;
private JPanel panel;
private MyObserver observer;
public ButtonPanel(){
panel = new JPanel();
panel.setPreferredSize(new Dimension(300, 40));
panel.setLayout(new FlowLayout());
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.setContentPane(panel);
yellow = new JButton("YELLOW");
yellow.setPreferredSize(new Dimension(100,30));
yellow.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
updateObserver(observer, 0);
}
});
orange = new JButton("ORANGE");
orange.setPreferredSize(new Dimension(100,30));
orange.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateObserver(observer, 1);
}
});
panel.add(yellow);
panel.add(orange);
pack();
setLocationRelativeTo(null);
}
#Override
public void addObserver(MyObserver obs) {
this.observer = obs;
}
#Override
public void updateObserver(MyObserver obs, int color) {
obs.update(color);
}
}
Finally, the customized observer and observable interfaces, note in the real app i use a table model not just an int - I'm not sure it's a good way -
package tests;
public interface MyObservable {
public void addObserver(MyObserver obs);
public void updateObserver(MyObserver obs, int color);
}
package tests;
public interface MyObserver {
public void update(int color);
}
CHANGED ANSWER:
In SidePanel.java add:
private MainFrame frame;
Then make your constructor take a MyFrame object as parameter. Do this:
public SidePanel(MainFrame frame){
this.frame = frame;
//rest not changed
//
}
Change the actionPerformed() of test button to:
test.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
buttonPanel.setVisible(true);
frame.initialise(0); // this line is added
}
});
In MainFrame.java:
Change sidePanel = new SidePanel(); to sidePanel = new SidePanel(this);
AND
Change private void initialise(int i) to public void initialise(int i)
This does what you are trying to achieve.
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.
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.