Problems with Swing CardLayout - java

I'm building a Java Virtual Pet game, currently the menu panel is working fine, buttons are loading and operating, but when I click my new game button which is supposed to launch into the GamePanel Panel I get a nullpointerException.
This is the main class which builds the original frame and Jpanel to switch from.
public class MainFrame extends JPanel {
public JPanel mainPanel;
public CardLayout cl;
private final GamePanel gamePanel;
private final MenuPanel menuPanel;
/**
* Constructs the main panel to be used to switch between panels.
*
*/
public MainFrame() {
// creates a new panel to add panels to.
cl = new CardLayout();
// panel to be used as a main switch.
mainPanel = new JPanel();
Dimension size = getPreferredSize();
size.width = 600;
size.height = 600;
setPreferredSize(size);
setBackground(Color.BLACK);
add(mainPanel);
gamePanel = new GamePanel();
menuPanel = new MenuPanel();
// sets layout
mainPanel.setLayout(cl);
mainPanel.add(menuPanel, "menuPanel");
mainPanel.add(gamePanel, "gamePanel");
}
public void changePanel(String name) {
cl.show(mainPanel, name);
}
/**
* Main frame used by the game.
*
* #param args
*/
public static void main(String[] args) {
MainFrame game = new MainFrame();
JFrame frame = new JFrame("Main Window");
frame.add(game);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
}
}
This is the code from my MenuPanel class which should be accessing the changePanel() method.
public class MenuPanel extends JPanel {
MainFrame mf;
public MenuPanel() {
this.mf = mf;
Dimension size = getPreferredSize();
size.width = 600;
size.height = 600;
setPreferredSize(size);
ImageIcon menuIcon = new ImageIcon("C:\\Programming\\NetBeansProjects\\PDCMain\\src\\Data\\the_menu_title2.png");
JLabel menuLbl = new JLabel();
menuLbl.setIcon(menuIcon);
JButton newGameBtn = new JButton("New Game");
JButton loadGameBtn = new JButton("Load Game");
JButton helpBtn = new JButton("Instructions");
JButton exitBtn = new JButton("Exit");
newGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New Game");
mf.changePanel("gamePanel");
}
});
loadGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
}
});
helpBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
showHelp();
}
});
exitBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.weightx = 0;
gc.weighty = 0.1;
gc.gridx = 0;
gc.gridy = 1;
add(menuLbl, gc);
gc.gridx = 0;
gc.gridy = 2;
add(newGameBtn, gc);
gc.gridx = 0;
gc.gridy = 3;
add(loadGameBtn, gc);
gc.gridx = 0;
gc.gridy = 4;
add(helpBtn, gc);
gc.gridx = 0;
gc.gridy = 5;
add(exitBtn, gc);
}
public void showHelp(){
String[] help = new String[100];
try {
BufferedReader br = new BufferedReader(new FileReader("instruct.txt"));
String line = "";
int i = 0;
while((line = br.readLine()) != null) {
help[i] = line;
i++;
}
JOptionPane.showMessageDialog(null, help);
} catch(IOException ex) {
System.err.println("IOException Error: " + ex.getMessage());
}
}
and here is the game panel
public class GamePanel extends JPanel{
private ButtonsPanel buttonsPanel;
private GraphicsPanel graphicsPanel;
private final JPanel gamePanel;
public GamePanel(){
super();
this._initGUI();
gamePanel = new JPanel();
Dimension size = getPreferredSize();
size.width = 600;
size.height = 600;
setPreferredSize(size);
setBackground(Color.RED);
}
private void _initGUI(){
this.buttonsPanel = new ButtonsPanel();
this.graphicsPanel = new GraphicsPanel();
this.setLayout(new BorderLayout());
this.add(buttonsPanel, BorderLayout.SOUTH);
this.add(graphicsPanel, BorderLayout.CENTER);
}
public void run(){
graphicsPanel.run();
}
}
This is my first attempt at building a GUI from scratch and using cardlayout. I can't see where it would be assigning a null value as the panel is declared in the main panel used for the switching, any ideas?

Take a second to look at this piece of code...
public static class MenuPanel extends JPanel {
MainFrame mf;
public MenuPanel() {
this.mf = mf;
Basically, you are assigning (this) mf to this.mf, but since mf is already null, this.mf will remain null...
You should be passing a reference of MainFrame to your MenuPanel
Now, because I hate passing around references of UI components to other parts of the program, which should have no idea about the rest of the UI and don't direct control over them, I would advice against passing MainFrame blinding like this.
Instead, create a interface which provides the functionality you expect that MenuPanel should be allowed to perform (newGame, loadGame, showInstructions, exit) for example and allow the implementation of this interface (probably the MainFrame) to deal with it
You would then pass the instance of this interface to MenuPanel...This limits the exposure of classes to other classes which don't need those classes functionality and decouples your code so you can pass any old instance of the interface to the class without having to rewrite whole sections of your code...
Updated with a basic concept
Start with some kind game controller interface, for example...
public interface GameController {
public void newGame();
public void loadGame();
public void showInstructions();
public void exit();
}
This describes the actions that can take against the implementation of this interface.
Use MainFrame and implement the GameController...
public class MainFrame extends JPanel implements GameController {
//...
public void newGame() {
}
public void loadGame() {
}
public void showInstructions() {
}
public void exit() {
}
Change MenuPane to require an instance of GameController when it's constructed...
public class MenuPanel extends JPanel {
private GameController controller;
public MenuPanel(GameController gameController) {
this.controller = gameController;
//...
Now, when you handle the user interaction, you would pass the request to the controller....
newGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New Game");
controller.newGame();
}
});
In this way, the MenuPanel doesn't know how a new game is created, it just knows that it will be taken care of...
Now, you will need to update the newGame method in MainFrame to do something, for example...
public void newGame() {
// Setup the new game...
cl.show(mainPanel, "gamePanel");
}
This places all the responsibility squarely with the implementation of the GameController to make decisions about how certain actions should take place, decouples the process, so if you change the implementation of the GameController to some other class, you don't need to worry about updating the MenuPanel, cause it's only interested in taking to the GameController...nice ;)

Related

Why does my Canvas not draw the rectangles

I know this problem has probably already been solved before but I don't really know how to describe the problem well, so I have a hard time finding it.
The problem I have is that I have a canvas that is attached to a panel and that panel is attached to a main panel and that to the frame. When I run the code the canvas does nothing. However if I dont add the button panel it works.
this is my code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ColorFrame extends JFrame {
private static final int FRAMEWIDTH = 400;
private static final int FRAMEHEIGHT = 400;
private int aORec = 4;
private Canvas canvas;
private JPanel mainPanel;
private JPanel panel;
private JPanel buttonPanel;
private JButton lessButton;
private JButton moreButton;
public ColorFrame() {
mainPanel = new JPanel();
panel = new JPanel();
canvas = new painter();
panel.add(canvas);
mainPanel.add(panel, BorderLayout.CENTER);
createComponents();
add(mainPanel);
setSize(FRAMEWIDTH,FRAMEHEIGHT);
setDefaultCloseOperation(3);
}
private void createComponents() {
buttonPanel = new JPanel();
lessButton = new JButton("Less");
moreButton = new JButton("More");
ActionListener bL = new ButtonListener();
lessButton.addActionListener(bL);
moreButton.addActionListener(bL);
buttonPanel.add(moreButton);
buttonPanel.add(lessButton);
mainPanel.add(buttonPanel, BorderLayout.NORTH);
}
class painter extends Canvas {
#Override
public void paint(Graphics g) {
for (int i = 0; i < aORec; i++) {
int tempWidth = (int)(Math.random() * (FRAMEWIDTH-0));
int tempHeight = (int)(Math.random() * (FRAMEHEIGHT -0));
g.drawRect(tempWidth,tempHeight,20,20);
}
}
}
class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == moreButton) {
aORec =+ aORec;
canvas.repaint();
} else {
if (aORec != 1) {
aORec -= (aORec*0.5);
canvas.repaint();
} else {
System.out.println("There are not enough rectangles to be drawn.");
}
}
}
}
}
Main panel is created by
mainPanel = new JPanel();
Components are being added to the main panel as
mainPanel.add(panel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.NORTH);
but no LayoutManager was set, so the default FlowLayout is used, not BorderLayout. Create the panel by
mainPanel = new JPanel(new BorderLayout());
or add the statement
mainPanel.setLayout(new BorderLayout());
This is also valid for panel (otherwise it will not be resized, that is, stay with size zero)
(BorderLayout is the default for JFrame(

How to completely get rid of a JPanel and everything in it?

How to completely get rid of a JPanel and everything in it while running?
package textgame;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class EscapeFromPrison extends JFrame implements ActionListener{
JButton startGameButton;
JButton creditsButton;
JButton choice1;
Button choice2;
JLabel mainTitleLabel;
JLabel question;
JLabel space;
JLabel credits;
JPanel titleScreen;
public EscapeFromPrison(){
super("Escape From Prison");
setLookAndFeel();
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainTitleLabel = new JLabel("<html>Escape From Prison</html>");
startGameButton = new JButton("<html>START<html>");
startGameButton.setActionCommand("StartGameButton");
creditsButton = new JButton("<html>CREDITS</html>");
creditsButton.addActionListener(this);
creditsButton.setActionCommand("CreditsButton");
question = new JLabel("");
space = new JLabel("");
credits = new JLabel("");
choice1 = new JButton("");
choice2 = new JButton("");
JPanel titleScreen = new JPanel();
BoxLayout titleScreenLayout = new BoxLayout(titleScreen, BoxLayout.Y_AXIS);
titleScreen.setLayout(titleScreenLayout);
titleScreen.add(mainTitleLabel);
titleScreen.add(startGameButton);
titleScreen.add(creditsButton);
add(titleScreen);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent buttonClick){
String event = buttonClick.getActionCommand();
In this if statement I want to get rid of mainTitleLabel, startGameButton, and creditsButton. So question can be in the top left position, cause right now they are currently invisible and the question is in the top right position. I am using grid layout.
if(event.equals("StartGameButton")){
GridLayout grid = new GridLayout(2,2);
setLayout(grid);
question.setText("<html>text</html>");
choice1.setActionCommand("");
choice1.setText("");
choice2.setActionCommand("");
choice2.setText("");
mainTitleLabel.setVisible(false);
startGameButton.setVisible(false);
creditsButton.setVisible(false);
add(question);
add(choice1);
add(choice2);
setVisible(true);
}
if(event.equals("CreditsButton")){
FlowLayout flo = new FlowLayout();
setLayout(flo);
credits.setText("<html></html>");
mainTitleLabel.setVisible(false);
startGameButton.setVisible(false);
creditsButton.setVisible(false);
add(credits);
setVisible(true);
}
}
private void setLookAndFeel(){
try{
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
);
}catch(Exception exc){
//ignore error
}
}
public static void main(String[] arguments){
EscapeFromPrison frame = new EscapeFromPrison();
}
}
The basic problem to your current code is startGameButton is never registered to an ActionListener, so it will never do anything.
The large issue is your code is going to become massively complicated to manage as you add more content. A better solution would be to separate each view in it's own class, which you can then manage independently and switch in and out as you need.
This is a beginning of the concept of Model-View-Controller, where you separate your functionality into self contained domains which can communicate via something like a observer pattern (as one idea).
With this you can use a CardLayout to make it easier to switch between views more simply.
Basic example...
This is just one of many possible ways to achieve this
First, we need to define some kind of "controller" which can be used to control what is been made visible. We uses interfaces as the primary contract as they limit the exposure of the implementation and define the exact contract that we expect to be maintained between class, this is commonly known as "program to interfaces", see Smarter Java development and Code to Interface for more details
public interface CardGameController {
public void showMainMenu();
public void showQuestion();
public void showCredits();
}
Now, because I want to use a CardLayout as the underlying mechanism for controlling the views, I need a implementation of the CardGameController which can do this...
public class DefaultCardGameController implements CardGameController {
public static final String MAIN_MENU_PANE = "mainMenuPane";
public static final String CREDITS_PANE = "creditsPane";
public static final String QUESTION_PANE = "questionPane";
private Container parent;
private CardLayout cardLayout;
public DefaultCardGameController(Container parent, CardLayout cardLayout) {
this.parent = parent;
this.cardLayout = cardLayout;
}
public Container getParent() {
return parent;
}
public CardLayout getCardLayout() {
return cardLayout;
}
protected void show(String name) {
getCardLayout().show(getParent(), name);
}
#Override
public void showMainMenu() {
show(MAIN_MENU_PANE);
}
#Override
public void showCredits() {
show(CREDITS_PANE);
}
#Override
public void showQuestion() {
show(QUESTION_PANE);
}
}
Have a look at How to Use CardLayout for more details
Now, we need the view we want to manage...
public class MenuPane extends JPanel {
private JButton startGameButton;
private JButton creditsButton;
private JLabel mainTitleLabel;
private CardGameController controller;
public MenuPane(CardGameController controller) {
this.controller = controller;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
mainTitleLabel = new JLabel("<html>Escape From Prison</html>");
startGameButton = new JButton("<html>START<html>");
startGameButton.setActionCommand("StartGameButton");
startGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
controller.showQuestion();
}
});
creditsButton = new JButton("<html>CREDITS</html>");
creditsButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
controller.showCredits();
}
});
creditsButton.setActionCommand("CreditsButton");
add(mainTitleLabel, gbc);
add(startGameButton, gbc);
add(creditsButton, gbc);
}
}
public class QuestionPane extends JPanel {
private JButton choice1;
private JButton choice2;
private JLabel question;
private CardGameController controller;
public QuestionPane(CardGameController controller) {
this.controller = controller;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
question = new JLabel("The meaning of life, the universe and everything!?");
choice1 = new JButton("42");
choice2 = new JButton("46");
add(question, gbc);
JPanel panel = new JPanel();
panel.add(choice1);
panel.add(choice2);
gbc.gridy++;
add(panel, gbc);
// Have some mechanism to control the questions
}
}
public class CreditsPane extends JPanel {
private JLabel credits;
private CardGameController controller;
public CreditsPane(CardGameController controller) {
this.controller = controller;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
credits = new JLabel("Happy Bunnies");
add(credits);
}
}
And finally, the "master" view which brings it all together...
public class EscapeFromPrison extends JPanel {
private MenuPane menuPane;
private QuestionPane questionPane;
private CreditsPane creditsPane;
public EscapeFromPrison() {
CardLayout cardLayout = new CardLayout();
setLayout(cardLayout);
DefaultCardGameController controller = new DefaultCardGameController(this, cardLayout);
menuPane = new MenuPane(controller);
questionPane = new QuestionPane(controller);
creditsPane = new CreditsPane(controller);
add(menuPane, DefaultCardGameController.MAIN_MENU_PANE);
add(questionPane, DefaultCardGameController.QUESTION_PANE);
add(creditsPane, DefaultCardGameController.CREDITS_PANE);
controller.showMainMenu();
}
private static void setLookAndFeel() {
try {
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
);
} catch (Exception exc) {
//ignore error
}
}
public static void main(String[] arguments) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
setLookAndFeel();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new EscapeFromPrison());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Now, this is a very limited example, which takes what you have already done and demonstrates some basic principles.
Because you'll be asking more then one question, you will need some way to manage the questions (and answers) more efficiently, this would be best done by some kind of model which contained all the questions, possible answers and the users answer. This way, you would get away with just a single question view, but which could use the information in the question model to reconfigure itself to display each new question
For example

Calling method from one class in other

I know that this kind of issue has been discussed here many times, but I'm confused. I'm totally beginner in Java and I honestly don't know what to do and I admit that I don't have that much time to read whole documentation provided by Oracle. Here's my problem:
I'm trying to program a GUI for my program that will be show interference of acoustic waves. Mathematical functionality doesn't matter in here. I've got two classes called Window and Sliders. Window is intended to be a 'main GUI class' and Sliders is supposed to inherit (?) from it.
This comes from another issue that I need to implement ActionListener in class Window and ChangeListener in Sliders class. I heard that one class can't implement several classes that's why I made two.
Now, I wrote a little bit chaotic those two classes, but I don't know how to combine them. It's really silly, but after C++ I'm pretty confused how to tell the program that it is supposed to show in one frame either buttons defined in Window class and sliders defined in Sliders class. Currently it shows only buttons I want to make it showing sliders too.
I'm very sorry for chaotic pseudo code, please help. Please, try to explain as simply as you can/possible. Please feel free to ignore overrided methods, they're not finished yet.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
public class Window extends JFrame implements ActionListener
{
private JButton showChord, playSound, getSample, getPlot;
private JLabel chordInfo;
private JPanel basicFunctions;
public Window()
{
init();
}
private void init()
{
setVisible(true);
setSize(new Dimension(1000,500));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
basicFunctions = new JPanel();
FlowLayout basicLayout = new FlowLayout();
basicFunctions.setLayout(basicLayout);
showChord = new JButton("Akord");
playSound = new JButton("Odtwórz");
getSample = new JButton("Pobierz dźwięk");
getPlot = new JButton("Pobierz wykres");
showChord.addActionListener(this);
playSound.addActionListener(this);
getSample.addActionListener(this);
getPlot.addActionListener(this);
basicFunctions.add(showChord);
basicFunctions.add(playSound);
basicFunctions.add(getSample);
basicFunctions.add(getPlot);
add(basicFunctions);
Sliders param = new Sliders();
}
public static void main(String[] args)
{
Window frame = new Window();
}
//Action Listener
#Override
public void actionPerformed(ActionEvent a)
{
Object event = a.getSource();
if(event == showChord)
{
}
else if(event == playSound)
{
}
else if(event == getSample)
{
}
else if(event == getPlot)
{
}
}
}
import java.awt.*;
import javax.swing.event.ChangeEvent;
import javax.swing.*;
import javax.swing.event.ChangeListener;
public class Sliders extends Window implements ChangeListener
{
private JPanel sliders, sliderSub;
private JTextField accAmplitude, accFrequency, accPhase;
private JSlider amplitude, frequency, phase;
private double amplitudeValue, frequencyValue, phaseValue;
public Sliders()
{
sliders = new JPanel();
sliders.setLayout(new FlowLayout());
amplitude = new JSlider(0,100,0);
amplitude.setMajorTickSpacing(10);
amplitude.setMinorTickSpacing(5);
amplitude.setPaintTicks(true);
amplitude.setPaintLabels(true);
frequency = new JSlider(0,10,0);
frequency.setMajorTickSpacing(1);
frequency.setMinorTickSpacing(1/10);
frequency.setPaintTicks(true);
frequency.setPaintLabels(true);
phase = new JSlider(0,1,0);
phase.setMinorTickSpacing(2/10);
phase.setPaintTicks(true);
phase.setPaintLabels(true);
accAmplitude = new JTextField();
accFrequency = new JTextField();
accPhase = new JTextField();
sliders.add(amplitude, BorderLayout.NORTH);
sliders.add(frequency, BorderLayout.CENTER);
sliders.add(phase, BorderLayout.SOUTH);
add(sliders);
}
#Override
public void stateChanged(ChangeEvent arg0)
{
}
}
I've done this so far, but those text fields just stopped showing sliders values and I don't know why. They are defined in the Parameters class and Window class. Can someone help? Additionally in the future I'd like to make those text fields editable so that you can set slider value by typing it in the text field.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.event.*;
public class Window extends JPanel
{
private JMenuBar mainMenu = new JMenuBar();
private Plot plot = new Plot();
private Parameters param = new Parameters();
private JComboBox chooseChord = new JComboBox();
private JButton playSound = new JButton("Odtwórz");
private JButton getSample = new JButton("Pobierz dźwięk");
private JButton getPlot = new JButton("Pobierz wykres");
private JPanel mainPanel = new JPanel();
private JPanel subPanel = new JPanel();
private JPanel buttonsPanel = new JPanel();
private JPanel slidersPanel = new JPanel();
private JLabel chord = new JLabel("Akord:");
private JTextField aValue = new JTextField();
private JTextField fValue = new JTextField();
private JTextField pValue = new JTextField();
public Window()
{
mainPanel.setLayout(new FlowLayout());
buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.Y_AXIS));
slidersPanel.setLayout(new BorderLayout());
subPanel.setLayout(new BorderLayout());
chooseChord.addItem("A");
chooseChord.addItem("A#");
chooseChord.addItem("Ab");
chooseChord.addItem("B");
chooseChord.addItem("Bb");
chooseChord.addItem("C");
chooseChord.addItem("C#");
chooseChord.addItem("Cb");
chooseChord.addItem("D");
chooseChord.addItem("D#");
chooseChord.addItem("E");
chooseChord.addItem("F");
chooseChord.addItem("F#");
buttonsPanel.add(chord);
buttonsPanel.add(chooseChord);
buttonsPanel.add(Box.createRigidArea(new Dimension(0,10)));
buttonsPanel.add(playSound);
buttonsPanel.add(Box.createRigidArea(new Dimension(0,10)));
buttonsPanel.add(getSample);
buttonsPanel.add(Box.createRigidArea(new Dimension(0,10)));
buttonsPanel.add(getPlot);
buttonsPanel.setBorder(BorderFactory.createTitledBorder("Menu"));
slidersPanel.add(param);
JMenu langMenu = new JMenu("Język");
mainMenu.add(langMenu);
subPanel.add(buttonsPanel, BorderLayout.NORTH);
subPanel.add(slidersPanel, BorderLayout.SOUTH);
mainPanel.add(subPanel);
mainPanel.add(plot);
add(mainPanel);
param.addAmplitudeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent a)
{
double ampValue = param.getAmplitudeValue();
aValue.setText(String.valueOf(ampValue));
}
}
);
param.addFrequencyListener(new ChangeListener()
{
public void stateChanged(ChangeEvent f)
{
double frValue = param.getFrequencyValue();
fValue.setText(String.valueOf(frValue));
}
}
);
param.addPhaseListener(new ChangeListener()
{
public void stateChanged(ChangeEvent p)
{
double phValue = param.getPhaseValue();
pValue.setText(String.valueOf(phValue));
}
}
);
}
public JMenuBar getmainMenu()
{
return mainMenu;
}
private static void GUI()
{
Window mainPanel = new Window();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setJMenuBar(mainPanel.getmainMenu());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
GUI();
}
}
);
}
}
class Parameters extends JPanel
{
private JPanel pane = new JPanel();
private JPanel ampPanel = new JPanel();
private JPanel frPanel = new JPanel();
private JPanel phPanel = new JPanel();
private JSlider amplitude = new JSlider(0,100,0);
private JSlider frequency = new JSlider(0,10000,0);
private JSlider phase = new JSlider(0,180,0);
private JLabel pLabel = new JLabel("Faza");
private JLabel aLabel = new JLabel("Amplituda (dB)");
private JLabel fLabel = new JLabel("Częstotliwość (Hz)");
private JTextField preciseAmplitude = new JTextField(3);
private JTextField preciseFrequency = new JTextField(4);
private JTextField precisePhase = new JTextField(3);
public Parameters()
{
preciseAmplitude.setEditable(true);
preciseFrequency.setEditable(true);
precisePhase.setEditable(true);
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
ampPanel.setLayout(new FlowLayout());
frPanel.setLayout(new FlowLayout());
phPanel.setLayout(new FlowLayout());
amplitude.setMajorTickSpacing(10);
amplitude.setMinorTickSpacing(5);
amplitude.setPaintTicks(true);
amplitude.setPaintLabels(true);
frequency.setMajorTickSpacing(2000);
frequency.setMinorTickSpacing(100);
frequency.setPaintTicks(true);
frequency.setPaintLabels(true);
phase.setMajorTickSpacing(2/10);
phase.setPaintTicks(true);
phase.setPaintLabels(true);
setBorder(BorderFactory.createTitledBorder("Parametry fali"));
ampPanel.add(aLabel);
ampPanel.add(preciseAmplitude);
pane.add(ampPanel);
pane.add(Box.createRigidArea(new Dimension(0,5)));
pane.add(amplitude);
pane.add(Box.createRigidArea(new Dimension(0,10)));
frPanel.add(fLabel);
frPanel.add(preciseFrequency);
pane.add(frPanel);
pane.add(Box.createRigidArea(new Dimension(0,5)));
pane.add(frequency);
pane.add(Box.createRigidArea(new Dimension(0,10)));
phPanel.add(pLabel);
phPanel.add(precisePhase);
pane.add(phPanel);
pane.add(Box.createRigidArea(new Dimension(0,5)));
pane.add(phase);
pane.add(Box.createRigidArea(new Dimension(0,10)));
add(pane);
}
public int getAmplitudeValue()
{
return amplitude.getValue();
}
public int getFrequencyValue()
{
return frequency.getValue();
}
public int getPhaseValue()
{
return phase.getValue();
}
public void addAmplitudeListener(ChangeListener ampListener)
{
amplitude.addChangeListener(ampListener);
}
public void addFrequencyListener(ChangeListener frListener)
{
frequency.addChangeListener(frListener);
}
public void addPhaseListener(ChangeListener phListener)
{
phase.addChangeListener(phListener);
}
}
class Plot extends JPanel
{
private JPanel componentWave = new JPanel();
private JPanel netWave = new JPanel();
private JLabel componentLabel = new JLabel("Fale składowe");
private JLabel netLabel = new JLabel("Fala wypadkowa");
private JLabel wave = new JLabel("Wybierz falę składową");
private JPanel labels = new JPanel();
private JComboBox chooseWave = new JComboBox();
public Plot()
{
labels.setLayout(new BoxLayout(labels, BoxLayout.PAGE_AXIS));
componentWave.setBackground(new Color(255,255,255));
netWave.setBackground(new Color(255,255,255));
componentWave.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
netWave.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
componentWave.setPreferredSize(new Dimension(400,200));
netWave.setPreferredSize(new Dimension(400,200));
labels.add(wave);
labels.add(Box.createRigidArea(new Dimension(0,10)));
labels.add(chooseWave);
labels.add(componentLabel);
labels.add(componentWave);
labels.add(Box.createRigidArea(new Dimension(0,20)));
labels.add(netLabel);
labels.add(netWave);
add(labels);
}
}
Window is intended to be a 'main GUI class' and Sliders is supposed to inherit (?) from it.
Nope: this is a misuse of inheritance and will only lead to problems since the Windows instance that Sliders inherently is, is completely distinct from the displayed Windows instance. What you need to do is to pass references.
For example, the following code uses outside classes for the JButton and JMenuItem Actions (Actions are like ActionListeners on steroids), and uses a class that holds a JSlider that allows itside classes to attach listeners to the slider.
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Foo extends JPanel {
private static final long serialVersionUID = 1L;
private Action helloAction = new HelloAction("Hello", KeyEvent.VK_H);
private Action exitAction = new ExitAction("Exit", KeyEvent.VK_X);
private JMenuBar menuBar = new JMenuBar();
private JTextField sliderValueField = new JTextField(10);
private Bar bar = new Bar();
public Foo() {
sliderValueField.setEditable(false);
sliderValueField.setFocusable(false);
add(new JButton(helloAction));
add(new JButton(exitAction));
add(new JLabel("Slider Value: "));
add(sliderValueField);
add(bar);
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic(KeyEvent.VK_F);
fileMenu.add(new JMenuItem(exitAction));
fileMenu.add(new JMenuItem(helloAction));
menuBar.add(fileMenu);
bar.addSliderListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int sliderValue = bar.getSliderValue();
sliderValueField.setText(String.valueOf(sliderValue));
}
});
}
public JMenuBar getJMenuBar() {
return menuBar;
}
private static void createAndShowGui() {
Foo mainPanel = new Foo();
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setJMenuBar(mainPanel.getJMenuBar());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class HelloAction extends AbstractAction {
public HelloAction(String name, int mnemonic) {
super(name); // sets name property and gives button its title
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Hello!");
}
}
class ExitAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public ExitAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Component component = (Component) e.getSource();
Window win = SwingUtilities.getWindowAncestor(component);
if (win == null) {
// if no window, then a JMenuItem held in a JPopupMenu
JPopupMenu popup = (JPopupMenu) component.getParent();
component = popup.getInvoker();
win = SwingUtilities.getWindowAncestor(component);
}
win.dispose();
}
}
class Bar extends JPanel {
private JSlider slider = new JSlider(0, 100, 50);
public Bar() {
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setSnapToTicks(true);
setBorder(BorderFactory.createTitledBorder("Slider Panel"));
add(slider);
}
public int getSliderValue() {
return slider.getValue();
}
// one way to let outside classes listen for changes
public void addSliderListener(ChangeListener listener) {
slider.addChangeListener(listener);
}
}
You ask about decimal labels, and yes this can be done but requires use of a label table. For example,
JSlider slider = new JSlider(0, 100, 50);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(2);
Dictionary<Integer, JLabel> labels = new Hashtable<>();
for (int i = 0; i <= 100; i += 20) {
labels.put(i, new JLabel(String.format("%.1f", i / 200.0)));
}
slider.setLabelTable(labels);
Which displays as:
You would also have to translate the value back from int to its corresponding floating point number.

A individual class for each Card in java swing CardLayout

For architecture and design purposes I would like to design my GUI with a class for each card in a Java Swing CardLayout. and then have a mainapp that builds the GUI. I am having trouble doing this right now.
I would like to example have a class for the main menu with all the button locations etc. and then just instantiate that card and add it to the layout in another class. Does anyone know how to achieve this?
Perhaps you want to give your class that uses the CardLayout a public loadCard method, something like
public void loadCard(JComponent component, String key) {
cardHolderPanel.add(component, key);
}
where cardHolderPanel is the container that holds the cards.
Since your creating classes to act as cards, consider having them all extend from a base abstract class or an interface that has a method that allows this class to hold its own key String. Either that or simply use the JComponent name property to have a component hold its own key String, one that can easily be obtained via getName().
For a more detailed answer, you may need to give us more details on your current application and its structure.
very simple example that held Swing Objects generated from different Java Classes
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class OnTheFlyImageTest extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel cardPanel;
private CardLayout cardLayout;
public OnTheFlyImageTest() {
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
cardLayout = new CardLayout(5, 5);
cardPanel = new JPanel(cardLayout);
cp.add(cardPanel);
for (int i = 0; i < 100; i++) {// Create random panels for testing.
String name = "ImagePanel" + (i + 1);
String image = (i & 1) == 0 ? "foo.gif" : "bar.gif";
ImagePanel imgPanel = new ImagePanel(name, image);
cardPanel.add(imgPanel, name);
cardLayout.addLayoutComponent(imgPanel, name);
}
JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 5, 5));
JButton prevButton = new JButton("< Previous");
prevButton.setActionCommand("Previous");
prevButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.previous(cardPanel);
}
});
buttonPanel.add(prevButton);
JButton nextButton = new JButton("Next >");
nextButton.setActionCommand("Next");
nextButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.next(cardPanel);
}
});
buttonPanel.add(nextButton);
JPanel temp = new JPanel(new BorderLayout());
temp.add(buttonPanel, BorderLayout.LINE_END);
cp.add(temp, BorderLayout.SOUTH);
setContentPane(cp);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("Test");
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new OnTheFlyImageTest().setVisible(true);
}
});
}
}
class ImagePanel extends JPanel {
private static final long serialVersionUID = 1L;
private String imgString;
private JLabel imgLabel;
public ImagePanel(String name, String imgString) {
setName(name);
this.imgString = imgString;
setLayout(new BorderLayout());
// Ensure size is correct even before any image is loaded.
setPreferredSize(new Dimension(640, 480));
}
#Override
public void setVisible(boolean visible) {
if (visible) {
System.err.println(getName() + ": Loading and adding image");
ImageIcon icon = new ImageIcon(imgString);
imgLabel = new JLabel(icon);
add(imgLabel);
}
super.setVisible(visible);
if (!visible) { // Do after super.setVisible() so image doesn't "disappear".
System.err.println(getName() + ": Removing image");
if (imgLabel != null) { // Before display, this will be null
remove(imgLabel);
imgLabel = null; // Hint to GC that component/image can be collected.
}
}
}
}

Java : Swing : Hide frame after button pressed

I have a button in a java frame that when pressed it reads a value from a text field and uses that string as a port name attempting to connect to a serial device.
If this connection is successful the method returns true if not it returns false. If it returns true I want the frame to disappear. A series of other frames specifed in other classes will then appear with options to control the serial device.
My problem is: the button is connected to an action listener, when pressed this method is invoked. If I try to use the frame.setVisible(true); method java throws a abstract button error because I'm effectively telling it to disappear the frame containing the button before the button press method has exited. Removing the frame.setVisible(true); allow the program to run correctly however I am left with a lingering connection frame that is no longer any use.
How to I get the frame to disappear upon pressing a the button?
package newimplementation1;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
*
* #author Zac
*/
public class ConnectionFrame extends JPanel implements ActionListener {
private JTextField textField;
private JFrame frame;
private JButton connectButton;
private final static String newline = "\n";
public ConnectionFrame(){
super(new GridBagLayout());
textField = new JTextField(14);
textField.addActionListener(this);
textField.setText("/dev/ttyUSB0");
connectButton = new JButton("Connect");
//Add Components to this panel.
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
add(textField, c);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(connectButton, c);
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
boolean success = Main.mySerialTest.initialize(textField.getText());
if (success == false) {System.out.println("Could not connect"); return;}
frame.setVisible(false); // THIS DOES NOT WORK!!
JTextInputArea myInputArea = new JTextInputArea();
myInputArea.createAndShowGUI();
System.out.println("Connected");
}
});
}
public void actionPerformed(ActionEvent evt) {
// Unimplemented required for JPanel
}
public void createAndShowGUI() {
//Create and set up the window.
frame = new JFrame("Serial Port Query");
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
//Add contents to the window.
frame.add(new ConnectionFrame());
frame.setLocation(300, 0);
//Display the window.
frame.pack();
frame.setVisible(true);
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent e) {
System.out.println("Exiting Gracefully");
Main.mySerialTest.close();
((JFrame)(e.getComponent())).dispose();
System.exit(0);
}
});
}
}
Running your snippet (after removing/tweaking around the custom classes), throws an NPE. Reason is that the frame you'r accessing is null. And that's because it's never set. Better not rely on any field, let the button find its toplevel ancestor and hide that, like in
public void actionPerformed(final ActionEvent e) {
boolean success = true;
if (success == false) {
System.out.println("Could not connect");
return;
}
Window frame = SwingUtilities.windowForComponent((Component) e
.getSource());
frame.setVisible(false); //no problem :-)
}
Your problem is with this line:
frame.add(new ConnectionFrame());
You're creating a new ConnectionFrame object, and so the frame that your button tries to close on is not the same as the one being displayed, and this is the source of your problem.
If you change it to,
//!! frame.add(new ConnectionFrame());
frame.add(this);
so that the two JFrames are one and the same, things may work more smoothly.
But having said that, your whole design smells bad and I'd rethink it in a more OOP and less static fashion. Also, use dialogs where dialogs are needed, not frames, and rather than dialogs consider swapping views (JPanels) via CardLayout as a better option still.
Myself, I'd create a "dumb" GUI for this, one that creates a JPanel (here in my example it extends a JPanel for simplicity, but I'd avoid extending if not necessary), and I'd let whoever is calling this code decide what to do with the information via some control. For e.g.,
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class ConnectionPanel extends JPanel {
private JTextField textField;
private JButton connectButton;
private ConnectionPanelControl control;
public ConnectionPanel(final ConnectionPanelControl control) {
super(new GridBagLayout());
this.control = control;
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.connectButtonAction();
}
}
};
textField = new JTextField(14);
textField.addActionListener(listener);
textField.setText("/dev/ttyUSB0");
connectButton = new JButton("Connect");
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
add(textField, c);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(connectButton, c);
connectButton.addActionListener(listener);
}
public String getFieldText() {
return textField.getText();
}
}
Again, something outside of the simple GUI would make decisions on what to do with the text that the textfield contains and what to do with the GUI that is displaying this JPanel:
public interface ConnectionPanelControl {
void connectButtonAction();
}
Also, you will likely do any connecting in a background thread so as to not freeze your GUI, probably a SwingWorker. Perhaps something like this:
import java.awt.event.ActionEvent;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyMain extends JPanel {
public MyMain() {
add(new JButton(new ConnectionAction("Connect", this)));
}
private static void createAndShowGui() {
JFrame frame = new JFrame("My Main");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MyMain());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class ConnectionAction extends AbstractAction {
private MyMain myMain;
private ConnectionPanel cPanel = null;
private JDialog dialog = null;
public ConnectionAction(String title, MyMain myMain) {
super(title);
this.myMain = myMain;
}
#Override
public void actionPerformed(ActionEvent e) {
if (dialog == null) {
dialog = new JDialog(SwingUtilities.getWindowAncestor(myMain));
dialog.setTitle("Connect");
dialog.setModal(true);
cPanel = new ConnectionPanel(new ConnectionPanelControl() {
#Override
public void connectButtonAction() {
final String connectStr = cPanel.getFieldText();
new MySwingWorker(connectStr).execute();
}
});
dialog.getContentPane().add(cPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
}
dialog.setVisible(true);
}
private class MySwingWorker extends SwingWorker<Boolean, Void> {
private String connectStr = "";
public MySwingWorker(String connectStr) {
this.connectStr = connectStr;
}
#Override
protected Boolean doInBackground() throws Exception {
// TODO: make connection and then return a result
// right now making true if any text in the field
if (!connectStr.isEmpty()) {
return true;
}
return false;
}
#Override
protected void done() {
try {
boolean result = get();
if (result) {
System.out.println("connection successful");
dialog.dispose();
} else {
System.out.println("connection not successful");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
Your code would be much more readable if you named JFrame instances xxxFrame, and JPanel instances xxxPanel. Naming JPanel instances xxxFrame makes things very confusing.
It would also help if you pasted the stack trace of the exception.
I suspect the problem comes from the fact that frame is null. This is due to the fact that the frame field is only initialized in the createAndShowGUI method, but this method doesn't display the current connection panel, but a new one, which thus have a null frame field:
ConnectionFrame firstPanel = new ConnectionFrame();
// The firstPanel's frame field is null
firstPanel.createAndShowGUI();
// the firstPanel's frame field is now not null, but
// the above call opens a JFrame containing another, new ConnectionFrame,
// which has a null frame field
The code of createAndShowGUI should contain
frame.add(this);
rather than
frame.add(new ConnectionFrame());
for Swing GUI is better create only once JFrame and another Top-Level Containers would be JDialog or JWindow(un-decorated by default),
simple example here
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SuperConstructor extends JFrame {
private static final long serialVersionUID = 1L;
public SuperConstructor() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(300, 300));
setTitle("Super constructor");
Container cp = getContentPane();
JButton b = new JButton("Show dialog");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
FirstDialog firstDialog = new FirstDialog(SuperConstructor.this);
}
});
cp.add(b, BorderLayout.SOUTH);
JButton bClose = new JButton("Close");
bClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
System.exit(0);
}
});
add(bClose, BorderLayout.NORTH);
pack();
setVisible(true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
SuperConstructor superConstructor = new SuperConstructor();
}
});
}
private class FirstDialog extends JDialog {
private static final long serialVersionUID = 1L;
FirstDialog(final Frame parent) {
super(parent, "FirstDialog");
setPreferredSize(new Dimension(200, 200));
setLocationRelativeTo(parent);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setModalityType(Dialog.ModalityType.DOCUMENT_MODAL);
JButton bNext = new JButton("Show next dialog");
bNext.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
SecondDialog secondDialog = new SecondDialog(parent, false);
}
});
add(bNext, BorderLayout.NORTH);
JButton bClose = new JButton("Close");
bClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
setVisible(false);
}
});
add(bClose, BorderLayout.SOUTH);
pack();
setVisible(true);
}
}
private int i;
private class SecondDialog extends JDialog {
private static final long serialVersionUID = 1L;
SecondDialog(final Frame parent, boolean modal) {
//super(parent); // Makes this dialog unfocusable as long as FirstDialog is visible
setPreferredSize(new Dimension(200, 200));
setLocation(300, 50);
setModal(modal);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setTitle("SecondDialog " + (i++));
JButton bClose = new JButton("Close");
bClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
setVisible(false);
}
});
add(bClose, BorderLayout.SOUTH);
pack();
setVisible(true);
}
}
}
better would be re-use Top-Level Containers, as create lots of Top-Level Containers on Runtime (possible memory lack)

Categories

Resources