How to add global JPanels to a JFrame - java

I am currently in a programming course in highschool and we are focusing on Java, one of the programs required to be built is a Rock Paper Scissors game. I have done this easily and it works but i decided to try and figure out how to make it work in a window of its own. this led me to research JFrames and how to use them. I have loked up many tutorials to introduce it and I have 5 different examples from the oracle site saved to use for reference, yet i have not be able to figure out why this program won't work.
package rps;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* Name: Steven Biro
* Course Code: ICS3U
* Teacher: Mr.Carron
* Date: 23-Apr-2014
* Program Description:
*/
public class RPS
implements ActionListener {
static JPanel text,buttons;
/**
* #param args the command line arguments
*/
public void RPS() {
JButton Rock,Paper,Scissors;
buttons = new JPanel();
Rock = new JButton("Rock");
Paper = new JButton("Paper");
Scissors = new JButton("Scissors");
Rock.setMnemonic(KeyEvent.VK_D);
Paper.setMnemonic(KeyEvent.VK_M);
Scissors.setMnemonic(KeyEvent.VK_E);
Rock.setActionCommand("Rock");
Paper.setActionCommand("Paper");
Scissors.setActionCommand("Scissors");
Rock.addActionListener(this);
Paper.addActionListener(this);
Scissors.addActionListener(this);
buttons.add(Rock);
buttons.add(Paper);
buttons.add(Scissors);
}
public void actionPerformed(ActionEvent e) {
String PC,Player;
int outcome;
PC="";
Player=(e.getActionCommand());
int computer = (int)(Math.random()*3+1);
if (computer==1) {
PC="Rock";
} else if (computer==2) {
PC="Paper";
} else {
PC="Scissors";
} if (Player.equals(PC)) {
outcome=0; //tied
} else {
if ("Rock".equals(PC)) {
if ("Paper".equals(Player)) {
outcome=1; //win
} else {
outcome=2; //lose
}
} else if ("Paper".equals(PC)) {
if ("Scissors".equals(Player)) {
outcome=1; //win
} else {
outcome=2; //lose
}
} else {
if ("Rock".equals(Player)) {
outcome=1; //win
} else {
outcome=2; //lose
}
}
}
JLabel r;
if (outcome==0) {
r = new JLabel ("You Tied.");
} else if (outcome==1) {
r = new JLabel ("You Win.");
} else if (outcome==2) {
r = new JLabel ("You Lose.");
} else {
System.exit(2);
r = new JLabel ("wont ever execute");
}
text = new JPanel();
text.add(r);
}
public static void GUI() {
//Create and set up the window.
JFrame frame = new JFrame("RockPaperScissors");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
frame.add(buttons);
frame.add(text);
//Display the window.
frame.pack();
frame.setVisible(true);
frame.setSize(250,150);
}
public static void main(String[] args) {
GUI();
}
}
If you can help me figure out why the JPanels called "buttons" and "text" wont add I would be very grateful.
If I remove
//Create and set up the content pane.
frame.add(buttons);
frame.add(text);
from my program, then it runs without a problem and is just a blank window as would be expected, so i am at a loss of what to do.
EDIT:
The error i get after i make the correction of removing void from public void RPS() {
Exception in thread "main" java.lang.NullPointerException
at java.awt.Container.addImpl(Container.java:1091)
at java.awt.Container.add(Container.java:1003)
at javax.swing.JFrame.addImpl(JFrame.java:564)
at java.awt.Container.add(Container.java:415)
at rps.RPS.GUI(RPS.java:102)
at rps.RPS.main(RPS.java:114)
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true
Java Result: 1
If i remove static from static JPanel text,buttons; then netbeans "corrects" each method so it never says static for any of them including Main so it says it cant find main.
sorry if im coming across as stupid, but if anyone could please help me figure this out i would be very appreciative.

You never create a RPS instance anywhere via new RPS().
You have a "pseudo-constructor" in your RPS class. i.e., this, public void RPS() { is not a constructor. Get rid of the void return type as constructors should have no return type: public RPS() {
Your Swing component fields should most definitely not be static.
You're adding components to a JFrame without respect for its layout manager, the BorderLayout. Your current code is adding null components, but if they weren't null, they'd both be added BorderLayout.CENTER, the last one covering up the previous one.
Create a master JPanel in the RPS class, and then add that to the JFrame in the main method, via non-static methods.
For example, RPS could potentially extend JPanel, and if that's the case, then you'd add it to the JFrame like so:
JFrame frame = new JFrame("RPS");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new RPS());
Other suggestions:
Check out enums as Rock Paper Scissors lends itself well to this, including giving the enum methods to test for win
By using an enum, it would be easier to modify the code later to allow for additional states, such as Lizard and Spock.
Try to separate the logic portion of your program from the view -- the GUI portion.
Avoid having your GUI classes implement your listener interfaces as this leads to creation of "switch-board" listeners, kind of like you're writing -- a bear to debug or enhance. Instead experiment with anonymous inner classes, or private inner classes here.

This is probably because
public void RPS() {
is not the constructor, but a method (because it has a return type), and so is never called.
Remove the void and you should be good.

Related

How to change JPanel color with ActionListener from another class?

I am having issues changing the color of a Jpanel (mainPanel) in one of my classes using an ActionListener for a radio button in a different class. I have the default color for mainPanel set to black.
Essentially I have a setMainPanel method in my first class:
public class MainBoard extends JFrame{
public void setMainPanel(Color c){
mainPanel.setBackground(c);
}
}
In my second class I have my action listener as:
private class MenuHandler implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource() == exit) {
System.exit(0);
}else if(e.getSource() == blueBackground) {
MainBoard mb = new MainBoard();
mb.setMainPanel(Color.BLUE);
}
}
}
My System.exit command will work if I click "Exit" in my menu. The concept I am trying to accomplish is to have a "Change Color" menu with radio buttons where the user can change to Black or Blue. I know this is probably overkill, but we are trying to learn how to make changes for components from different classes.
Thank you for any help!
No good:
MainBoard mb = new MainBoard(); // this is not the currently displayed MainBoard
mb.setMainPanel(Color.BLUE);
This represents a common Java newbie error of magical thinking where you think that changing the state of an object of a class will magically change the state of a different object of the same class, but this is not how Java works. You need to call the setMainPanel(...) method on a reference to the currently displayed or active MainBoard instance, not some random new and totally distinct MainBoard object that you create within this method and which is never displayed.
So pass in the appropriate reference into the listener, perhaps using something like:
private class MenuHandler implements ActionListener {
MainBoard mainBoard;
MenuHandler(MainBoard mainBoard) {
this.mainBoard = mainBoard;
}
public void actionPerformed(ActionEvent e){
if(e.getSource() == exit) {
System.exit(0);
}else if(e.getSource() == blueBackground) {
// MainBoard mb = new MainBoard();
mainBoard.setMainPanel(Color.BLUE);
}
}
}

Java - Number Game - Multiple ActionListener in the Same Class

I am only 6 - 7 weeks into learning Java, so I apologize in advance if my code is sloppy or terminology is off. I'm trying to create program that creates a random number and allows the user to guess until they get the correct number. It serves no real purpose other than a learning experience for me.
I have the basic program working, I just want to add other elements to improve it and gain the experience.
The program runs in a JFrame and has a JTextField for the user to enter their guess. I have ActionListener setup for the JTextField. I'd like to add a Start button that displays at the beginning of the game. When the user clicks the start button, the JTextField should become active. Also, when the user clicks guesses the correct answer, I'd like to use the start button to reset the program. I've experimented with several ways to do this with no success. I believe this will require multiple ActionListeners in the same class. I'm not even sure if this is possible?
Here is my code. Thanks in advance for any help.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class JMyFrame2 extends JFrame implements ActionListener {
Random num = new Random();
int computerGenerated = num.nextInt(1000);
public int userSelection;
JTextField numberField = new JTextField(10);
JLabel label1 = new JLabel();
Container con = getContentPane();
int previousGuess;
// constructor for JMyFrame
public JMyFrame2(String title) {
super(title);
setSize(750, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label1 = new JLabel(
"I have a number between 1 and 1000 can you guess my number?" + "Please enter a number for your first guess and then hit Enter.");
setLayout(new FlowLayout());
add(numberField);
add(label1);
System.out.println(computerGenerated);
numberField.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
userSelection = Integer.parseInt(numberField.getText());
con.setBackground(Color.red);
if (userSelection == computerGenerated) {
label1.setText("You are correct");
con.setBackground(Color.GREEN);
} else if (userSelection > computerGenerated) {
label1.setText("You are too high");
} else if (userSelection < computerGenerated) {
label1.setText("You are too low");
}
}
}
public class JavaProgram5 {
public static void main(String[] args) {
JMyFrame2 frame2 = new JMyFrame2("Assignment 5 - Number Guessing Game");
frame2.setVisible(true);
}
}
Sure you can have multiple action listeners.
In fact, your class should not implement it.
Start by removing the actionPerformed method,
and replace this line:
numberField.addActionListener(this);
With this:
numberField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
userSelection = Integer.parseInt(numberField.getText());
con.setBackground(Color.red);
if (userSelection == computerGenerated) {
label1.setText("You are correct");
con.setBackground(Color.GREEN);
} else if (userSelection > computerGenerated) {
label1.setText("You are too high");
} else if (userSelection < computerGenerated) {
label1.setText("You are too low");
}
}
});
You can add another action listener to the start button you plan to add,
following this pattern, using an anonymous class.
(It doesn't have to be an anonymous class,
this was just simple to demonstrate.)
As janos said, adding an action listener to each button does the job well, but in large code when you need to add a lot of buttons it doesn't look too neat, I suggest you using the setActionCommand() for the JButtons that you are creating, the usage is simple, you implement ActionListener to the JFrame as you did but after each button add
button.addActionListener(this);
button.setActionCommand("commandname")
And you can do it for as much as buttons as you wish, now to fire these commands correctly your action performed function should look like this:
#Override
public void actionPerformed (ActionEvent e) {
String cmd = e.getActionCommand();
switch(cmd) {
case "action1":
// Do something
break;
case "action2":
// Do something else
break;
case "potato":
// Give Mr. chips a high five
break;
default:
// Handle other cases
break;
}
}
And so on, again the other solution works perfectly fine, I just personally think that this one is a lot neater especially in a code where you have multiple action listeners.

I'm probably doing this incorrectly. Is there a better way?

So usually when I make mock-up programs (like this one) I look for things I can improve on in case the situation happens again.
Today I thought I'd brush up on basic OOP (I understand the concept of OOP, just haven't messed around with it for a bit and wanted to freshen my memory). So I decided to make a little game that just creates 3 monsters on a 10x10 plane and 1 player (you), you are able to move your player in any x/y direction. My program works but I can't help but feel that I'm doing something incorrectly.
So the basic layout of my program was to have 5 classes. A GUI class that shows the game and gives you directional buttons for movement control, a class that creates the monsters, a class that creates the players, a class that creates the 10x10 board and keeps track of monster/player locations, and of course a main class that creates all the objects and has the main game loop and whatnot.
I was having a bit of a hard time interacting with my main class and my GUI class. What I ended up doing was doing a while loop in my main class and waiting until the player presses the start button, and once the player presses it (via action listener) the GUI class sets a public variable (running) from false to true, and I am able to act accordingly once the variable is changed.
HERE is where I feel like I am doing something wrong: At first my while loop would not terminate unless I printed out to the console. I Googled the issue and apparently people have said that it's some sort of issue with threading or "active polling", which I did not understand. I went to my program and added a small 10ms thread sleep in my while loops and everything started working great.
My question to you guys is, what is active polling? Why is it bad? How/why/where was this going on in my program? And finally if there's a better way of interacting with a GUI class and a main class. Sorry for the giant wall of text but I like to be thorough when explaining a situation!
TL;DR: Am I interacting correctly with my GUI class and my main class? If not what is the proper way to do it?
My main class:
public class MainGame {
public static void main(String[] args) throws InterruptedException{
ShowGUI gui = new ShowGUI();
while(!gui.running){
Thread.sleep(10);
}
Board gameBoard = new Board();
gui.setLabelText(gameBoard.getBoard());
//Add Player
Player playerOne = new Player(1000, "Player 1");
//Add monsters
Monster monstMatt = new Monster(1000, "Matt");
Monster monstJon = new Monster(1000, "Jon");
Monster monstAbaad = new Monster(1000, "Abaad");
while(gui.running){
Thread.sleep(10);
int x, y;
x = playerOne.getX();
y = playerOne.getY();
if(gui.buttonPress != -1){
if(gui.buttonPress == 1){
playerOne.move(x, --y);
}else if(gui.buttonPress == 2){
playerOne.move(x, ++y);
}else if(gui.buttonPress == 3){
playerOne.move(--x, y);
}else if(gui.buttonPress == 4){
playerOne.move(++x, y);
}
gui.buttonPress = -1;
gui.setLabelText(gameBoard.getBoard());
}
}
}
}
My GUI Class:
public class ShowGUI{
private JTextArea board;
private JButton moveUp;
private JButton moveDown;
private JButton moveLeft;
private JButton moveRight;
public boolean running = false;
public int buttonPress = -1;
public ShowGUI(){
System.out.println("GUI Successfully Loaded");
createAndShow();
}
private void createAndShow(){
JFrame mainFrame = new JFrame("Bad Game");
addComponents(mainFrame.getContentPane());
mainFrame.setSize(500, 400);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setLocationRelativeTo(null);
mainFrame.setResizable(false);
mainFrame.setVisible(true);
}
private void addComponents(Container pane){
pane.setLayout(null);
board = new JTextArea(1, JLabel.CENTER);
moveUp = new JButton("Up");
moveDown = new JButton("Down");
moveLeft = new JButton("Left");
moveRight = new JButton("Right");
moveUp.setBounds(185, 225, 130, 35);
moveLeft.setBounds(115, 280, 130, 35);
moveRight.setBounds(255, 280, 130, 35);
moveDown.setBounds(185, 335, 130, 35);
board.setEditable(false);
board.setBounds(115, 30, 270, 145);
board.setFont(new Font("Consolas", Font.BOLD, 12));
addActionListeners();
pane.add(board);
pane.add(moveUp);
pane.add(moveRight);
pane.add(moveLeft);
pane.add(moveDown);
}
private void addActionListeners(){
moveUp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
running = true;
buttonPress = 1;
}
});
moveDown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 2;
}
});
moveLeft.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 3;
}
});
moveRight.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 4;
}
});
}
public void setLabelText(char[][] boardToShow){
board.setText(" ");
for(int i = 0; i < boardToShow.length; i++){
for(int j = 0; j < boardToShow[i].length; j++){
board.append(boardToShow[i][j] + " ");
}
board.append("\n ");
}
}
}
If you require my Board/Monster/Player classes I can post them, but I don't think the problem is with those classes.
Active polling (a.k.a. busy waiting) is when a program checks over and over whether some condition is true, and since we're talking about computers, this is usually done many times a second. It's bad because it means the process is constantly using up CPU to check for this condition, and, even worse, because it uses up so much of the CPU, it can prevent the condition from being able to become true in the first place (this is what is happening in your case).
I'll try to explain this with a metaphor, where your Main class (specifically your while(gui.running) loop) is the boss, and the GUI class is the employee. Imagine the boss comes to his employee every second and asks "Have you done what I asked you to do?". By doing so every second, not only is the boss wasting his time, but is actually preventing his employee from being able to do what he was asked to do.
That is exactly what is happening between your Main and GUI class. The value of buttonPress can not change when the while loop keeps running, and that's why sleeping (and printing to console, because that requires an IO operation which also blocks the thread for a while) makes it work, because the while loop stops executing for a short amount of time, giving your program the chance to change the value of buttonPress.
The solution
In order to solve this, let's go back to the boss/employee metaphor. Instead of checking every second, the boss should say, "When you are done doing this, come and tell me". That way he doesn't need to keep checking on the employee and the employee has time to do the task. Similarly, your GUI class should call a method in your Main class when the actionListeners are fired. Ultimately, as other people pointed out, your structure needs quite a bit of work and clean up, I would recommend you look into using the Model-View-Controller pattern.
However I will propose a solution which will work for the setup you currently have. Your Main class should look something like:
public class Main {
private Player playerOne;
private Monster monstMatt;
private Monster monstJon;
private Monster monstAbaad;
private ShowGUI gui;
private Board gameBoard;
public Main() {
//Add Player
playerOne = new Player(1000, "Player 1");
//Add monsters
monstMatt = new Monster(1000, "Matt");
monstJon = new Monster(1000, "Jon");
monstAbaad = new Monster(1000, "Abaad");
gui = new ShowGUI(this);
gameBoard = new Board();
gui.setLabelText(gameBoard.getBoard());
}
public movePlayerUp() {
movePlayer(0, -1);
}
public movePlayerDown() {
movePlayer(0, 1);
}
public movePlayerLeft() {
movePlayer(-1, 0);
}
public movePlayerRight() {
movePlayer(1, 0);
}
private movePlayer(x, y) {
playerOne.move(playerOne.getX() + x, playerOne.getY() + y);
}
}
And the GUI class:
public class ShowGUI {
private Main main;
public ShowGui(Main main) {
this.main = main;
createAndShow();
System.out.println("GUI Successfully Loaded");
}
private void addActionListeners(){
moveUp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerUp();
}
});
moveDown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerDown();
}
});
moveLeft.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerLeft();
}
});
moveRight.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerRight();
}
});
}
/* All the other methods you have */
...
}
So instead of using flags (buttonPress=2), use methods which will be called when a certain action occurs. Again, this is not a perfect long-term solution, but it has the right gist and is the sort of pattern you should follow.

JComboBox changinging other JComboBoxes (multiple times) error

need some help with ComboBoxes in Java. Looked through similar questions, found one slightly related, but not what im dealing with.
I need to load certain arrays into combo boxes depending on the items selected in the precious combo box:
think getting some procedure done at a medical center: Choose a procedure->get a list of doctors who do it, choose a doctor->get a list of available hours etc.
A single choice is working fine(whether it's "procedure->list of doctors", or "list of doctors->their working hours"), but doing more than a single one of those changes doesn't work.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
public class GUIbandymas extends JFrame {
String[] start={"Choose","Choice1", "Choice2"};
String[] Option1={"Choose","A1"};
String[] Option2={"Choose","A2","A3"};
String[] Option3={"Choose","a","b","c","d"};
String[] Option4={"Choose","1","2","3","4"};
String[] Option5={"Choose","I","II","III","IV"};
String[] pradinis={"Pasirinkite Laika"};
String[] p1={"Pasirinkite Gydytoja"};
static double kainaR;
static double kainaK;
JComboBox<String> G=new JComboBox<String>(p1);
JComboBox<String> proc;
JComboBox<String> laikas=new JComboBox<String>(pradinis);
JComboBox<String> minutes;
JButton button = new JButton ("Registuotis");
JLabel label = new JLabel("Moketi uz vizita");
JLabel suma = new JLabel();
public GUIbandymas() throws Exception {
setValueProc(start);
frame();
}
public void frame()
{
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(500,300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(proc);
panel.add(G);
panel.add(laikas);
panel.add(button);
button.setEnabled(false);
//panel.add(minutes);
frame.add(panel);
panel.add(label);
panel.add(suma);
proc.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(proc.getSelectedItem().toString().equals("Choice1"))
{
setGyd(Option1);
}
if(proc.getSelectedItem().toString().equals("Choice2"))
{
setGyd(Option2);
}
}
});
G.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent a) {
if(G.getSelectedItem().toString().equals("A1"))
{
setLaikas(Option3);
}
if(G.getSelectedItem().toString().equals("A2"))
{
setLaikas(Option4);
}
if(G.getSelectedItem().toString().equals("A3"))
{
setLaikas(Option5);
}
}
});//JComboBox
}
public void setGyd(String[] s)
{
G.removeAllItems();
for(int i=0; i<s.length; i++)
{
G.addItem(s[i]);
}
}
public void setValueProc(String[] sarasas)
{
proc=new JComboBox<String>(sarasas);
}
public void setLaikas(String[] sarasas)
{
laikas.removeAllItems();
for(int i=0; i<sarasas.length; i++)
{
laikas.addItem(sarasas[i]);
}
}
}
Im in a dire need of any suggestions and possible fixes, im inclined to think that it has something to do with action listeners, since methods do work, but im at a loss since i cant determine what is it.
EDITED: the actual code should work, seems like there is no unneeded things from other files left.
NOTE: this is work with GUI, just launch it in you main() :)
While I don't really like the if-else approach you're using, it should work just fine. I agree with rrirower's suggestion that you should look at using a data model instead. Especially if you get a lot of choices, since the code turns messy quite fast.
The problem with your code is that you run into NullPointerException when rebuilding the combobox items. The reason for this is that G.actionPerformed() is called when you remove/add items. After you have removed all items (before you start adding new ones), G.getSelectedItem() will return null.
If you code a little bit more defensively, then it works as expected:
proc.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object selectedItem = proc.getSelectedItem();
if ("Choice1".equals(selectedItem)) {
setGyd(Option1);
}
if ("Choice2".equals(selectedItem)) {
setGyd(Option2);
}
}
});
G.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent a) {
Object selectedItem = G.getSelectedItem();
if ("A1".equals(selectedItem)) {
setLaikas(Option3);
}
if ("A2".equals(selectedItem)) {
setLaikas(Option4);
}
if ("A3".equals(selectedItem)) {
setLaikas(Option5);
}
}
});//JComboBox
Instead of checking for null's, I just flipped the equals and skipped the unnecessary toString() (they are already strings, that's what you put in there).
Another thing, a pet peeve of mine, please follow the normal java code convention for all your class, field and method names. You're almost there, but Option1 etc. should start with lowercase. G should probably have a more descriptive name, as well as start with lowercase.
Finally, I didn't understand why you both create a JFrame in the constructor and extend JFrame in your class. You should choose one or the other.
You don't appear to be using a data model for the combo box. The data model controls the internal list of items. Have a look at this for more info.

Java - Separate Listener Class throws NullPointerException

Apologize for the long post, but I wanted to be as detailed as possible so there's a better chance people understand what I'm trying to convey:
OK, so an overview of this problem is that I'm trying to make a program that simulates a cash register. (This is a "for-fun" project). The way I set it up is:
CashRegister: main class that starts the program, serves as the main window for everything.
public class CashRegister extends JFrame {
...
}
Other classes: serve as JPanels that provide the different components of the main CashRegister window. Example:
public class NorthPanel extends JPanel {
...
}
Then in the CashRegister class:
add(new NorthPanel(), BorderLayout.NORTH);
etc.
Basically, I have a JTextField in the NorthPanel class called priceField that holds the value of the price the user enters. I have a separate class (Keypad) that also extends JPanel and serves as the number keypad in the center of the main window. In CashRegister:
add(new NorthPanel(), BorderLayout.NORTH);
add(new Keypad()); // (default for BorderLayout is CENTER)
One of the problems I have run into is that I created one class, EventHandler, to serve as the listener class for the entire program because there are components in each JPanel class (NorthPanel and Keypad, for instance) that need to communicate with each other; the user presses a keypad button, the price field in the NorthPanel needs to know what key in the keypad was pressed.
I don't know if the problem comes from the fact that these components are coming from different classes and are therefore referenced differently, or what. All the classes are in the same directory, I'm using NetBeans and it's all part of the same package.
In my EventHandler class, I created several different constructors so as to be able to pass in all the components that need to communicate with each other from different classes. For example:
public class EventHandler implements ActionListener {
private JTextField priceField;
private JButton[][] keypad;
public EventHandler(JTextField priceField) {
this.priceField = priceField;
}
public EventHandler(JButton[][] keypad) {
this.keypad = keypad;
}
}
In my NorthPanel class, I instantiate priceField first, configure it (set the font, etc.) and say:
EventHandler e = new EventHandler(priceField);
in my attempt to pass the priceField through to my listener class.
Then in the Keypad class, I say:
EventHandler e = new EventHandler(keypad);
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 3; j++) {
keypad[i][j].addActionListener(e);
}
}
Then in the EventHandler class, having passed through those variables:
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 3; j++) {
if(keypad[i][j] == e.getSource()) {
// call a custom method to append the numbers to the text field
}
}
}
}
It is at this point that I get a NullPointerException. I don't know why, but my guess is that it's because the listener objects are coming from different classes and using different constructors because I have to pass in different objects from each class that need to communicate with each other. I'm not 100% sure though. Is there a way to get around this or am I doing something completely wrong?
You should let the CashRegister handle all user input, as it is the main Frame of your application (it extends the JFrame). For the sub-panels, make a method, such as aKeyWasPressed( int keyCode ), that will be called from your frame, within the body of keyPressed method you override.
Also, set the JFrame to be focusable, and preferably make the other panels unfocusable setFocusable(false);
Here is a sample code of the CashRegister:
public class CashRegister extends JFrame implements KeyListener
{
private NorthPanel northPanel;
private Keypad keypad;
public CashRegister ()
{
this.setFocusable(true);
this.addKeyListener(this);
northPanel = new NorthPanel();
keypad = new Keypad();
/* Your code ... */
}
#Override
public void keyTyped(KeyEvent ke)
{
/*Do nothing*/
}
#Override
public void keyPressed(KeyEvent ke)
{
northPanel.aKeyWasPressed( ke.getKeyCode() );
keypad.aKeyWasPressed( ke.getKeyCode() );
}
#Override
public void keyReleased(KeyEvent ke)
{
/*Do nothing*/
}
}
Note: To distinguish which key was pressed, inside the aKeyWasPressed method from your panels, you can do:
private void aKeyWasPressed(int code)
{
if(code == KeyEvent.VK_A)
{
/*If its letter 'A', do this...*/
}
}
Again, there are many ways of doing it, but letting the Frame itself handle all user input is, in my opinion, the best pratice in most cases, mainly when dealing with multiple panels.

Categories