I'm writing a Java programm, according to MVC model.
So the problem is that the Frame doesn't react to button click.
(The text, that I write isn't added to the TextArea after click)
At first I call constructors for View and Controller
MessageFrame mf = new MessageFrame(con);
MessageFrameListener mfl = new MessageFrameListener(mf);
Here is the part of MessageFrameListener class (controller)
public class MessageFrameListener{
private MessageFrame mf;
public MessageFrameListener(MessageFrame m_f){
mf = m_f;
m_f.addButtonListener(new SButtonListener());
}
//#Override
public class SButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
String insert = mf.getInput();
mf.addLine(insert);
mf.refreshInput();
}
}
}
Here is the part from MessageFrame class (View)
public class MessageFrame{
public JTextField messField;
public JTextArea dialogArea;
public JButton sendButton;
public JFrame frame;
public Contact con;
public MessageFrame (Contact con_get) {
con = con_get;
frame = new JFrame();
frame.setSize(538, 299);
JPanel panel_1 = new JPanel();
frame.getContentPane().add(panel_1, BorderLayout.NORTH);
JPanel panel_2 = new JPanel();
frame.getContentPane().add(panel_2, BorderLayout.SOUTH);
panel_2.setLayout(new BoxLayout(panel_2, BoxLayout.X_AXIS));
messField = new JTextField();
panel_2.add(messField);
messField.setColumns(10);
JButton sendButton = new JButton("Send");
panel_2.add(sendButton);
JPanel panel_3 = new JPanel();
frame.getContentPane().add(panel_3, BorderLayout.EAST);
JPanel panel_4 = new JPanel();
frame.getContentPane().add(panel_4, BorderLayout.CENTER);
panel_4.setLayout(new BorderLayout(0, 0));
JTextArea dialogArea = new JTextArea();
panel_4.add(dialogArea);
frame.setVisible(true);
}
public String getInput(){
return messField.getText();
}
public void refreshInput(){
messField.setText("");
}
public void addLine(String line){
dialogArea.append(line);
}
public void addButtonListener(ActionListener bal){
sendButton.addActionListener(bal);
}
}
You will definately find the answer if you check the output of your program or debug it.
Exception in thread "main" java.lang.NullPointerException
at test3.MessageFrame.addButtonListener(Main.java:93)
at test3.MessageFrameListener.<init>(Main.java:28)
at test3.Main.main(Main.java:18)
Your are hiding the reference to the JButton sendButton by declaring it again in the constructor so the field is never initialised.
JButton sendButton = new JButton("Send");
panel_2.add(sendButton);
Since you've posted code scraps and have not posted a functioning SSCCE that we can test, all we can do is guess -- so you'll get what you paid for, and here goes my guess:
You're listening on the wrong MessageFrame. Your program has 2 or more MessageFrame objects, one of which is displayed, and the other which is being listened to, and so your displayed MessageFrame will of never trip the listener.
If this doesn't help, and you need better help, then please provide us with a better question, and an sscce.
You are adding an empty string:
String insert = mf.getInput(); //all it does is: messField.getText();
mf.addLine(insert); //adding the empty string
mf.refreshInput(); //all it does is: messField.setText("");
Related
In trying to read the text that is entered into a textField, I used the actionlistener for a button right next to it. In this actionlistener class, I had an action performed method in which I created a string that was set equal to the textField.getText();. This class however has a problem recognizing textField variable from the previous class.
It is necessary for the .getText() or reading of the textField entry to be in the actionlistener class. I do not know what to try besides the code that I have listed down below.
public class MainClass {
public static void main(String args[]) {
JFrame frame = new JFrame ("Welcome");
frame.setVisible(true);
frame.setSize(500, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.add(panel);
JLabel label = new JLabel("...");
panel.add(label);
JTextField text = new JTextField(20);
panel.add(text);
JButton SubmitButton = new JButton("Analyze");
panel.add(SubmitButton);
SubmitButton.addActionListener(new Action1());
}
static class Action1 implements ActionListener {
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
JFrame frame1 = new JFrame("Word Commonality");
frame1.setVisible(true);
frame1.setSize(500,200);
String ReceivedPath = text.getText();
System.out.println(ReceivedPath);
Error is present at second to bottom line of code. The error is "text cannot be resolved"
I expect that the text can be read and printed out in the console.
Your problem is revolved around function scoping to fix it you need a direct access to the JTextField object you can do so by instantiating a new action performed straight in the MainClass like this:
public class Main {
public static void main(String args[]) {
new MainClass();
}
}
Here I created a class only used to instantiate the window class
For the main class I suggest extending JFrame so you can inherit all of it methods.
//Imports
public class MainClass extends JFrame {
private JPanel panel;
private JLabel label;
private JTextField text;
private JButton SubmitButton;
public MainClass(){
super("Welcome");
setSize(500, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
add(panel);
label = new JLabel("...");
panel.add(label);
text = new JTextField(20);
panel.add(text);
SubmitButton = new JButton("Analyze");
panel.add(SubmitButton);
SubmitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String ReceivedPath = text.getText();
System.out.println(ReceivedPath);
}
});
setVisible(true);
}
}
This is how your class should look like.
Side notes:
Set visible is at the end otherwise the items will not be see able.
The MainClass is inheriting from JFrame so it can use all its methods without instatiating it look at inheritance(https://www.w3schools.com/java/java_inheritance.asp)
The action performed now can acces the text JTextField because it is a class attribute.
If the solution is correct please think of marking this answer as final. Thank you
If you place the getText() outside the ActionListener, it will be read immediately after creating the panel. That is why it is empty. You can make the ActionListener assign a value to a variable, but it will be empty until the action is performed.
Also see here: Swing GUI doesn't wait for user input
The first popup would only send one action, the second would send two, third will be three so and so forth. I was able to narrow it down to it being the button sending action multiple time.
At first, I was using jframe for all my window, so I tried using jdialog, the problem persists. tried making it so that when the user clicks on the button the window is disposed, still don't fix it.
public class BoothDetails extends JDialog implements ActionListener{
FloorPlanGUI floorPlan = new FloorPlanGUI();
static JLabel bname = new JLabel();
JTextArea details = new JTextArea();
static JButton addsche = new JButton("ADD TO SCHEDULE");
JPanel northPanel = new JPanel();
public BoothDetails(String name, String detail) {
setVisible(true);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
bname.setText(name);
details.setText(detail);
setLayout(new BorderLayout());
northPanel.setLayout(new FlowLayout(0, 10, 10));
northPanel.add(bname);
northPanel.add(addsche);
addsche.addActionListener(floorPlan);
addsche.addActionListener(this);
add(northPanel, BorderLayout.NORTH);
add(details, BorderLayout.CENTER);
}
public void actionPerformed(ActionEvent a) {
dispose();
}
}
static JLabel bname = new JLabel();
JTextArea details = new JTextArea();
static JButton addsche = new JButton("ADD TO SCHEDULE");
Don't use the static keyword. This means the variable is shared by all instances of the class.
So every time you create a new instance of the class you execute the following code:
addsche.addActionListener(floorPlan);
which adds another ActionListener to the button.
The static keyword is should generally only be used when you create constant variables in your class, it should NOT be used for components which need to be unique for each class.
I am trying to do some simple method chaining but always have the error "cannot find symbol" part way through.
e.g.:
public JButton[] getSignOnButtons() {
return InitialFrame.getInitialPanel().getSignOnButtons();
}
I am implementing the MVC model, in the View Package I have 4 classes: View, InitialFrame, InitialPanel, NorthPanel. For my Controller to communicate with the View package, I always go through the View Class.
My Controller Class needs to access attributes of View's classes, what's the best way?
I "cheated" it previously by making all the View classes' attributes public so I could just create a 'get' method from view
e.g.
return InitialFrame.InitialPanel.Buttons;
Thanks for any help.
The error just says "cannot not find symbol" is each case.
**EDITED from this point down......
This is the whole View Package:
public class View {
InitialFrame initialFrame;
public View(){
initialFrame = new InitialFrame();
}
public JFrame getInitialFrame() {
return initialFrame;
}
public InitialPanel getInitialPanel() {
return InitialFrame.getInitialPanel();
}
public JButton[] getSignOnButtons() {
return initialFrame.getInitialPanel().getSignOnButtons();
}
}
This is the InitialFrame Class:
public final class InitialFrame extends JFrame {
private final InitialPanel initialPanel;
public InitialFrame() {
super("Welcome to the Sign-on Screen");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(700, 700);
this.setLayout(new GridLayout(1, 1));
initialPanel = new InitialPanel();
this.add(initialPanel);
//this.pack();
this.setLocationRelativeTo(null);
this.validate();
this.repaint();
this.setVisible(true);
JButton[] test = initialPanel.getSignOnButtons();
String newStr = initialPanel.getNorthPanel().getTest(); //Call to getTest
}
public InitialPanel getInitialPanel() {
return initialPanel;
}
}
//InitialPanel ___________
class InitialPanel extends JPanel{
private BorderLayout InitialPanelLayout;
private JButton[] signOnButtons;
private NorthPanel northPanel;
private JPanel southPanel;
private JPanel leftPanel;
private JPanel rightPanel;
private JPanel centerPanel;
private JLabel userNameLabel;
private JTextField userNameTextField;
public InitialPanel() {
this.setSize(600, 600);
InitialPanelLayout = new BorderLayout();
this.setLayout(InitialPanelLayout);
this.createPanels();
this.formatCenterPanel();
setVisible(true);
this.validate();
this.repaint();
}
/**
* Method is to create panels for all the Border Layout of initial Panel
* #param none
*/
private void createPanels() {
//Graphics comp2D = new Graphics();
//comp2D.drawString("Free the bound periodicals", 22, 100);
northPanel = new NorthPanel();
northPanel.setSize(600, 200);
this.add(northPanel, "North");
southPanel = new JPanel();
this.add(southPanel, "South");
leftPanel = new JPanel();
this.add(leftPanel, BEFORE_LINE_BEGINS);
rightPanel = new JPanel();
this.add(rightPanel, AFTER_LINE_ENDS);
centerPanel = new JPanel();
this.add(centerPanel, "Center");
}
/**
* Method is to format the center panel on the opening window.
* It uses 4 row grid layout, top row is a container with Label and TextField.
* #param none
*/
private void formatCenterPanel() {
centerPanel.setLayout(new GridLayout(5, 1));
Container userName = new Container();
userName.setLayout(new GridLayout(1, 2));
userNameLabel = new JLabel("UserName: ");
userName.add(userNameLabel);
userNameTextField = new JTextField(30);
userName.add(userNameTextField);
centerPanel.add(userName);
signOnButtons = new JButton[3];
signOnButtons[0] = new JButton("Sign-On");
signOnButtons[1] = new JButton("Register");
signOnButtons[2] = new JButton("Exit");
for (JButton butt: signOnButtons) {
centerPanel.add(butt);
}
centerPanel.validate();
centerPanel.repaint();
}
public JButton[] getSignOnButtons() {
return signOnButtons;
}
public JTextField getUserNameTextField() {
return userNameTextField;
}
public NorthPanel getNorthPanel() {
return northPanel;
}
}
ALL updated now...
Only one error remains: "non-static method getInitialPanel() cannot be referenced from a static context"
in View Class
public InitialPanel getInitialPanel() {
return InitialFrame.getInitialPanel();
}
Final Edit:
The main solution was to use the 'this' keyword.
After that, I could use Controller to chain 3 or more methods to retrieve attributes buried in the View Package.
for example, in the View Class:
public JButton[] getSignOnButtons() {
return this.initialFrame.getInitialPanel().getSignOnButtons();
}
**EDIT 12/25/2018
the this.keyword does not solve this every time. It is still a tricky operation.
Sometimes I just allowed NetBeans to create the method itself because it says method not found even though it is named exactly the same.
Method chaining works from left to right in way that value returned by left method must have right method implemented in the class;
for example;
" Hello ".substring(1).trim()
Notice that here substring(1) is returning String value, which has also trim() method implemented in its (String) class.
In your case;
return InitialFrame.getInitialPanel().getSignOnButtons();
you are returning Initialframe from getInitialPlane() method, but there is not implementation of getSignOnButtons() in InitialFrame class. That's why JVM is complaining.
There is no method getSignOnButtons() in JPanel. Perhaps it's in InitialPanel? if that is the case, the return type of InitialFrame.getInitialPanel() should be InitialPanel instead of JPanel.
This is happening because you are trying to execute a method which is not present in JFrame class. When you are declaring this JFrame InitialFrame;, at compile time Java will check whether the method exists or not in JFrame class. Since it is not there in JFrame, you looks to be getting "cannot not find symbol" error.
Also, try to follow code standards - field names should be in lower camel case i.e. intialFrame.
Try below code (I have not compiled it though) -
public class View {
private InitialFrame initialFrame;
public View(){
initialFrame = new InitialFrame();
}
public JFrame getInitialFrame() {
return this.initialFrame;
}
public JPanel getInitialPanel() {
return this.initialFrame.getInitialPanel();
}
public JButton[] getSignOnButtons() {
return this.initialFrame.getInitialPanel().getSignOnButtons();
}
}
public final class InitialFrame extends JFrame {
private InitialPanel initialPanel;
public InitialFrame() {
super("Welcome to the Sign-on Screen");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(700, 700);
this.setLayout(new GridLayout(1, 1));
initialPanel = new InitialPanel();
this.add(initialPanel);
//this.pack();
this.setLocationRelativeTo(null);
this.validate();
this.repaint();
this.setVisible(true);
JButton[] test = initialPanel.getSignOnButtons();
String new = initialPanel.getNorthPanel().getTest();
}
public JPanel getInitialPanel() {
return this.initialPanel;
}
}
I assume you have another InitialPanel class which is extending JPanel and having getSignOnButtons() method defined in it.
I have to add dynamically jcheckboxes in a panel when the user write something into a form. That's my code
Main
public class EmptyFrame extends JFrame{
private static final long serialVersionUID = 1L;
/*top panels*/
//to add technicians
private JButton newTechnician;
private NewTechForm ntForm;
private JPanel panelForm;
private JPanel panelChekBoxes;
TechCheckBoxGroup techniciansGroup;
private List<String> technicians;
//main container
private Container pane = getContentPane();
//components
GroupLayout gl = new GroupLayout(pane);
EmptyFrame(){
preinit();
init();
}
private void preinit(){
panelChekBoxes=new JPanel();
panelForm=new JPanel();
techniciansGroup=new TechCheckBoxGroup(panelChekBoxes);
}
private void init(){
/*top options*/
ntForm=new NewTechForm(panelForm);
newTechnician=new JButton("Add technician");
newTechnician.addActionListener(
new AddTechnicianAction(techniciansGroup,ntForm)
);
ntForm.getPanel().add(newTechnician);
/*end top options*/
for(String technic : technicians){
techniciansGroup.addCheckBoxes(
new JCheckBox(technic));
}
createWindowLayout(
new JLabel("Technicians"),
techniciansGroup.getCheckBoxes(),
ntForm.getPanel());
}
public void createWindowLayout(JComponent... arg) {
pane = getContentPane();
gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createParallelGroup()
.addGroup(gl.createSequentialGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addComponent(arg[2])
)
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(arg[0])
.addGroup(gl.createParallelGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addComponent(arg[2]))
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
EmptyFrame ex = new EmptyFrame();
ex.setVisible(true);
});
}
}
in the main are presents a form and a checkbox group the first is the ntForm and the second one is the techniciansGroup. When i insert a name inside the form i would like to add a checkbox inside the checkbox group, here are the button, the checkbox group and the form classes:
AddTechnicianAction
this would be the class where everything would happened
public class AddTechnicianAction implements ActionListener{
TechCheckBoxGroup technicians;
NewTechForm form;
JTable table;
public AddTechnicianAction(TechCheckBoxGroup arg0, NewTechForm arg1){
technicians=arg0;
form=arg1;
}
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Add new tech: "+this.form.getSurnameText().getText()+" "+this.form.getNameText().getText());
technicians.addCheckBoxes(new JCheckBox(this.form.getSurnameText()+" "+this.form.getNameText()));
System.out.println(technicians);
}
}
NewTechForm
this is the form
public class NewTechForm {
private JLabel nameLabel;
private JLabel surnameLabel;
private JTextField nameText;
private JTextField surnameText;
private JPanel panel;
public NewTechForm(JPanel panel){
nameLabel= new JLabel("Nome: ", JLabel.RIGHT);
surnameLabel = new JLabel("Cognome: ", JLabel.CENTER);
nameText = new JTextField(6);
surnameText = new JTextField(6);
this.panel=panel;
panel.add(nameLabel);
panel.add(nameText);
panel.add(surnameLabel);
panel.add(surnameText);
}
public JLabel getNameLabel() {
return nameLabel;
}
public JTextField getNameText() {
return nameText;
}
public JTextField getSurnameText() {
return surnameText;
}
public JLabel getSurnameLabel() {
return surnameLabel;
}
public JPanel getPanel() {
return panel;
}
}
The problem is that inside TechCheckBoxGroup something happens, but not the things that i'm expecting to. The panel have a new checkbox after the action is performed but it seems that that panel (the obne inside TachCheckBoxGroup) is not the one inside the main class, and infact nothing were rendered in the window. There is clearly something that i didn't understand about the scoping in swing, what's the better practice to do what i'm trying to? Or this is the good way and i miss something?
I think that it is important to have answers on stack overflow, so for that reason I post my answer to my problem even if it's not really easy to see that was the right solution, now I explain better. While I'm trying to solve the problem of the wrong behaviour I've asked my self if I was doing the wrong considerations, and so it was, because i was trying to let comunicate all the components without a real
mediator
in fact it was impossible to catch the event in the main window with the code that i had written above. Seaching and searching i finally find this great answer here. So i basically change the NewTechForm classes making it a jpanel with a form inside, same thing for the CheckGroupBox, i'll made it a panel with the check box inside, and i send all the event to a listener in the main window.
I'm trying to make a little game that will first show the player a simple login screen where they can enter their name (I will need it later to store their game state info), let them pick a difficulty level etc, and will only show the main game screen once the player has clicked the play button. I'd also like to allow the player to navigate to a (hopefully for them rather large) trophy collection, likewise in what will appear to them to be a new screen.
So far I have a main game window with a grid layout and a game in it that works (Yay for me!). Now I want to add the above functionality.
How do I go about doing this? I don't think I want to go the multiple JFrame route as I only want one icon visible in the taskbar at a time (or would setting their visibility to false effect the icon too?) Do I instead make and destroy layouts or panels or something like that?
What are my options? How can I control what content is being displayed? Especially given my newbie skills?
A simple modal dialog such as a JDialog should work well here. The main GUI which will likely be a JFrame can be invisible when the dialog is called, and then set to visible (assuming that the log-on was successful) once the dialog completes. If the dialog is modal, you'll know exactly when the user has closed the dialog as the code will continue right after the line where you call setVisible(true) on the dialog. Note that the GUI held by a JDialog can be every bit as complex and rich as that held by a JFrame.
Another option is to use one GUI/JFrame but swap views (JPanels) in the main GUI via a CardLayout. This could work quite well and is easy to implement. Check out the CardLayout tutorial for more.
Oh, and welcome to stackoverflow.com!
Here is an example of a Login Dialog as #HovercraftFullOfEels suggested.
Username: stackoverflow Password: stackoverflow
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import javax.swing.*;
public class TestFrame extends JFrame {
private PassWordDialog passDialog;
public TestFrame() {
passDialog = new PassWordDialog(this, true);
passDialog.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new TestFrame();
frame.getContentPane().setBackground(Color.BLACK);
frame.setTitle("Logged In");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
});
}
}
class PassWordDialog extends JDialog {
private final JLabel jlblUsername = new JLabel("Username");
private final JLabel jlblPassword = new JLabel("Password");
private final JTextField jtfUsername = new JTextField(15);
private final JPasswordField jpfPassword = new JPasswordField();
private final JButton jbtOk = new JButton("Login");
private final JButton jbtCancel = new JButton("Cancel");
private final JLabel jlblStatus = new JLabel(" ");
public PassWordDialog() {
this(null, true);
}
public PassWordDialog(final JFrame parent, boolean modal) {
super(parent, modal);
JPanel p3 = new JPanel(new GridLayout(2, 1));
p3.add(jlblUsername);
p3.add(jlblPassword);
JPanel p4 = new JPanel(new GridLayout(2, 1));
p4.add(jtfUsername);
p4.add(jpfPassword);
JPanel p1 = new JPanel();
p1.add(p3);
p1.add(p4);
JPanel p2 = new JPanel();
p2.add(jbtOk);
p2.add(jbtCancel);
JPanel p5 = new JPanel(new BorderLayout());
p5.add(p2, BorderLayout.CENTER);
p5.add(jlblStatus, BorderLayout.NORTH);
jlblStatus.setForeground(Color.RED);
jlblStatus.setHorizontalAlignment(SwingConstants.CENTER);
setLayout(new BorderLayout());
add(p1, BorderLayout.CENTER);
add(p5, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
jbtOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (Arrays.equals("stackoverflow".toCharArray(), jpfPassword.getPassword())
&& "stackoverflow".equals(jtfUsername.getText())) {
parent.setVisible(true);
setVisible(false);
} else {
jlblStatus.setText("Invalid username or password");
}
}
});
jbtCancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
parent.dispose();
System.exit(0);
}
});
}
}
I suggest you insert the following code:
JFrame f = new JFrame();
JTextField text = new JTextField(15); //the 15 sets the size of the text field
JPanel p = new JPanel();
JButton b = new JButton("Login");
f.add(p); //so you can add more stuff to the JFrame
f.setSize(250,150);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Insert that when you want to add the stuff in. Next we will add all the stuff to the JPanel:
p.add(text);
p.add(b);
Now we add the ActionListeners to make the JButtons to work:
b.addActionListener(this);
public void actionPerforemed(ActionEvent e)
{
//Get the text of the JTextField
String TEXT = text.getText();
}
Don't forget to import the following if you haven't already:
import java.awt.event*;
import java.awt.*; //Just in case we need it
import java.x.swing.*;
I hope everything i said makes sense, because sometimes i don't (especially when I'm talking coding/Java) All the importing (if you didn't know) goes at the top of your code.
Instead of adding the game directly to JFrame, you can add your content to JPanel (let's call it GamePanel) and add this panel to the frame. Do the same thing for login screen: add all content to JPanel (LoginPanel) and add it to frame. When your game will start, you should do the following:
Add LoginPanel to frame
Get user input and load it's details
Add GamePanel and destroy LoginPanel (since it will be quite fast to re-create new one, so you don't need to keep it memory).