Looking at other answers, i have followed exactly what they say, but i just keep getting the nullPointerException error. I have 4 classes, the 2 below, a GUI class and main menu class. Main manages the card layout and i would like a button in the Insert class to change the "Active" card to main menu class.
Main:
public class Main extends JPanel implements ChooserListener{
MainMenu mm;
Insert InsertCustomer;
public JPanel mPanel;
CardLayout cl;
private String c;
public Main(){
super();
//add mPanel, set to CardLayout and add the Main
mPanel = new JPanel();
this.add(mPanel);
cl = new CardLayout();
mPanel.setLayout(cl);
//add classes
mm = new MainMenu(this);
InsertCustomer = new Insert();
//add classes to mPanel
mPanel.add(mm, "mm");
mPanel.add(InsertCustomer, "InsertCustomer");
}
public void tell(Object o) {
c = o.toString();
cl.show(mPanel, c);
}
public void swapView(String key) {
CardLayout cl = (CardLayout)(mPanel.getLayout());
cl.show(mPanel, key);
}
}
Insert:
public class Insert extends JPanel{
private JButton logoutbutton;
private LogoutListener lListener;
public Insert() {
super();
//BUTTONS
//logout button
JButton logoutbutton = new JButton("Main Menu");
this.add(logoutbutton);
lListener = new LogoutListener(null);
logoutbutton.addActionListener(lListener);
}
private class LogoutListener implements ActionListener{
private Main main;
public LogoutListener(Main main){
this.main = main;
}
public void actionPerformed(ActionEvent e) {
main.swapView("mm");
}
}
}
lListener = new LogoutListener(null);
Your LogoutListener takes your Main-class, but you give him null. Of course you will get a NullPointerException (at least on your logoutButton-click).
Your problem in next lines :
lListener = new LogoutListener(null);
main.swapView("mm");
You need to put reference to your Main class, not null as you done. Because of your main in LogoutListener is null and you catch NPE.
Simple solution is to transfer reference of your Main to Insert with help of constructor and then transfer that to LogoutListener.
Related
I can add/remove elements to/from a panel and repaint it when the method used to fill the panel is called by one of its parent JFrame events, but I can not repaint it by events from other classes even if their sources have been added to it, or that is how I understand the problem for now.
I want to understand what is going on here, Thank you.
Main Class
public class Principal extends JFrame implements ActionListener{
private static Principal instPrincipal = null;
private SubClass subClassInst =new SubClass();
public JPanel panelPrincipal;
public static Principal getInstance() {
if (instPrincipal != null)
return instPrincipal ;
else {
instPrincipal = new Principal ();
return instPrincipal ;
}
}
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
try {
if(source == btnSub)
{
subClassInst.fillPanelPrincipal();
}
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
Sub Classes Example
public class SubClass implements ActionListener {
private JPanel tempPanel;
private JButton btnSave;
private Principal instPrincipal;
public void fillPanelPrincipal() {
instPrincipal = Principal.getInstance();
instPrincipal.panelPrincipal.removeAll();
//Start adding elements..
tempPanel = new JPanel();
instPrincipal.panelPrincipal.add(tempPanel);
btnSave = new JButton("Save");
btnSave.addActionListener(this);
tempPanel.add(btnSave);
//End.
instPrincipal.panelPrincipal.repaint();
}
public void actionPerformed(ActionEvent event) {
instPrincipal = Principal.getInstance();
Object source = event.getSource();
if (source == btnSave) {
// modify local data, Database .. ; //work but need to be repainted on panelPrincipal
instPrincipal.panelPrincipal.repaint();//does not work
}
}
}
Update
To clarify the problem more, I have one single JPanel on a JFrame and there are different classes to fill it for multiple functionalities, I call their methods using JMenuItems on the main frame, these Classes implement ActionListener, passing the panel didn't work, and also the method I am trying here.
I thought about changing the design to use CardLayout, but it was very difficult.
You are calling Principal as a static reference, so how is it supposed to know what frame to repaint? You should pass the instance of the JFrame through the constructor of the subclass. Like so:
private SubClass subClassInst = new SubClass(this);
And create the constructor like this
private JFrame parent;
public SubClass(JFrame parent) { this.parent = parent; }
You can then use it like so
this.parent.repaint();
I have a complicated GUI with lot of components (JButtons, JLabels, JComboBoxes, JSpinners, etc). That's why I have to split it on several classes (add components to JPanels, this JPanels add to bigger JPanels, this JPanels add to JTabbedPane, and JTabbedPane add to JFrame).
Depend on user choises and filling in data some components enabled or disabled or get some value and set not editable (in a word - interact). It's easy to done and worked properly, if components (which are interact) are in the same class, but if only it are in different classes - any results... AAA!!!
I made simple example to explane what I need. There are four classes. First one create JFrame and add JTabbedPane:
public class MainFrame extends JFrame {
MainFrame() {
super("MainFrame");
go();
}
public void go() {
Tabs tabs = new Tabs();
getContentPane().add(tabs);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 300);
setVisible(true);
}
public static void main(String[] args) {
MainFrame frame = new MainFrame();
}
}
The second class create JTabbedPane and add two JPanels as tabs. Second tab.setEnabledAt(1, false):
public class Tabs extends JTabbedPane {
public Tabs() {
go();
}
public void go() {
TabData data = new TabData();
add(" Data ", data);
TabCalculation calculation = new TabCalculation();
add("Calculation", calculation);
setEnabledAt(1, false);
}
}
The third class create JPanel with JComboBox:
public class TabData extends JPanel {
public TabData() {
go();
}
JComboBox someData;
public void go() {
String type[] = { " ", "Type 1", "Type 2", "Type 3" };
someData = new JComboBox(type);
add(someData);
someData.addActionListener(new DataListener());
}
public class DataListener implements ActionListener {
public void actionPerformed(ActionEvent ev) {
if (someData.getSelectedIndex() > 0) {
Tabs tabs = new Tabs();
tabs.setEnabledAt(1, true);
}
}
}
}
... and fourth class create some JPanel. Second tab with this JPanel disabled. When user set some value in JComboBox (selectedIndex>0) - tab have to enabled. But Tabs tabs = new Tabs(); tabs.setEnabledAt(1, true); didn't help...
How can I do that? PLEASE HELP!!! I can't sleep... I can't work... I always thinking about it and try to find out a solution...
When user set some value in JComboBox (selectedIndex>0) - tab have to
enabled.
If you need to have all of these classes split, then I would suggest you make this change in your 3rd class:
public class TabData extends JPanel {
JComboBox someData;
...
// Get rid of DataListener class and add this public method instead:
public void addActionListenerToComboBox(ActionListener listener) {
someData.addActionListener(listener);
}
}
And make this change in your 2nd class:
public class Tabs extends JTabbedPane {
public Tabs() {
go();
}
public void go() {
TabData data = new TabData();
data.addActionListenerToComboBox(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JComboBox comboBox = (JComboBox)e.getSource();
boolean enableSecondTab = comboBox.getSelectedIndex() > -1;
setEnabledAt(1, enableSecondTab);
}
});
add(" Data ", data);
TabCalculation calculation = new TabCalculation();
add("Calculation", calculation);
setEnabledAt(1, false);
}
}
Take a look to EventObject.getSource() javadoc for more details.
I'm programming in Java, trying to use a cardholder in order to switch between 2 JPanels which are each an extension of their own class. I think I understand the basic concepts but I am having errors in my current revision, when calling the classes. I'm getting a null pointer exception and I think it's a structural problem but I'm not sure how or why.
The main method points to this class
public class Skeleton implements ActionListener{
JPanel cardHolder;
CardLayout cards;
String cardA = "A";
String cardB = "B";
JPanel Jboard;
JPanel Jmenu;
JFrame frame2;
Board board;
Menu menu;
boolean menuSet;
boolean boardSet;
Timer timer;
public class Switcher implements ActionListener{
String card;
Switcher(String card){
this.card = card;
}
#Override
public void actionPerformed(ActionEvent e) {
cards.show(cardHolder, card);
}
}
public Skeleton(JFrame frame){
JPanel menu = new Menu();
JPanel board = new Board();
JFrame frame2 = frame;
timer = new Timer(5, this);
timer.start();
cardHolder = new JPanel();
cards = new CardLayout();
cardHolder.setLayout(cards);
cardHolder.add(menu, cardA);
cardHolder.add(board, cardB);
frame2.add(cardHolder);
frame2.revalidate();
frame2.setVisible(true);
}
public JFrame getSkeleton(){
return frame2;
}
public JPanel getCardHolder(){
return cardHolder;
}
public void checkStatus(){
if (menuSet == true){
new Switcher(cardB);
boardSet = false;
}
if (boardSet == true){
new Switcher(cardA);
menuSet = false;
}
}
#Override
public void actionPerformed(ActionEvent e) {
menuSet = menu.getMenuset();
boardSet = board.getBoardset();
checkStatus();
}
}
This is the board class, one of the JPanels I'm trying to switch between
public class Board extends JPanel{
boolean boardset;
Menu menu = new Menu();
public Board(){
setBackground(Color.WHITE);
}
public JPanel getPanel(){
return this;
}
public void setBoardset(boolean x){
boardset = x;
}
public boolean getBoardset(){
return boardset;
}
}
Here is the other JPanel class, which contains a button used to switch to the other JPanel class. This is also the original starting JPanel used.
public class Menu extends JPanel implements ActionListener{
boolean menuset;
public Menu(){
setBackground(Color.BLACK);
JButton button = new JButton("hello");
button.addActionListener(this);
this.add(button);
}
public JPanel getPanel(){
return this;
}
#Override
public void actionPerformed(ActionEvent e) {
menuset = true;
}
public void setMenuset(boolean x){
menuset = x;
}
public boolean getMenuset(){
return menuset;
}
}
Like I said, I'm getting a null pointer exception. It is occuring on this line of the Skeleton() class
menuSet = menu.getMenuset();
The line above is right after the actionPerformed event above (from the timer), and I have tested the timer a little, it works doing basic print statements but whenever I try to use the 'menu' or 'board' instance inside the actionPerformed, I get this null pointer exception.
I would appreciate any advice. I get the idea that the way I'm doing this may be a little convoluted. If anyone has any suggestions on a better way to do this it would also be helpful. My main goal is to be able to call 2 separate classes from one main class containing a cardholder. That way I can separate the code in order to keep everything isolated and in order.
Your Skeleton class has a "menu" member but it isn't set anywhere that I can see. The constructor declares its own "menu" local variable, which is local to the constructor and hides the member. Setting "menu" inside the constructor won't set the member. I don't see anywhere else where the "menu" member is set, unless I've missed something or unless another class in the same package is setting it.
Hi well my code so far does something like this: Click a button, opens a combobox. I want to select an option on the ComboBox and depending on which option is picked i want to open another combobox using getSelectIndex().
Here are parts of my code which are relevant. I know I have to make the other comboboxes not visible or removed but at the moment I'm just trying to make a combobox appear. As you can see i have inserted the actionlistener for the button which works and opens the combobox.however when selecting a string in the combobox no event occurs. However when I run it, no comboboxes appear.
public class Work extends JFrame {
// variables for JPanel
private JPanel buttonPanel;
private JButton timeButton;
public Work()
{
setLayout(new BorderLayout());
buttonPanel = new JPanel();
buttonPanel.setBackground(Color.RED);
buttonPanel.setPreferredSize(new Dimension(400, 500));
add(buttonPanel,BorderLayout.WEST);
timeButton = new JButton("Time");
buttonPanel.add(timeButton);
buttontime clickTime = new buttontime(); // event created when time button is clicked
timeButton.addActionListener(clickTime);
Time timeObject = new Time();
timeObject.SelectTime();
buttontime2 selectDest = new buttontime2();
timeObject.getAirportBox().addActionListener(selectDest);
}
public class buttontime implements ActionListener { //creating actionlistener for clicking on timebutton to bring up a combobox
public void actionPerformed(ActionEvent clickTime) {
Time timeObject = new Time();
timeObject.SelectTime();
add(timeObject.getTimePanel(),BorderLayout.EAST);
timeObject.getTimePanel().setVisible(true);
timeObject.getTimePanel().revalidate() ;
timeObject.getAirportBox().setVisible(true);
}
}
public class buttontime2 implements ActionListener{
public void actionPerformed(ActionEvent selectDest) {
Time timeObject = new Time();
timeObject.SelectTime();
if(timeObject.getAirportBox().getSelectedIndex() == 1) {
timeObject.getEastMidBox().setVisible(true);
}
else if(timeObject.getAirportBox().getSelectedIndex() == 2) {
timeObject.getBirmBox().setVisible(true);
}
else if(timeObject.getAirportBox().getSelectedIndex() == 3) {
timeObject.getMancbox().setVisible(true);
}
else if(timeObject.getAirportBox().getSelectedIndex() == 4) {
timeObject.getHeathBox().setVisible(true);
}
}
}
public static void main (String args[]) {
events mainmenu = new events(); //object is created
mainmenu.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainmenu.setSize(800,500);
mainmenu.setVisible(true);
mainmenu.setLayout(new BorderLayout());
mainmenu.setTitle("Learning how to use GUI");
mainmenu.setBackground(Color.BLUE);
mainmenu.setResizable(false);
}
}
my other class TIME
import javax.swing.JOptionPane;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Time
{
private JComboBox timeAirportbox;//comboboxes declared
private JComboBox eastMidbox;
private JComboBox mancBox;
private JComboBox heathBox;
private JComboBox birmBox;
private String[] airport = {"","EM", "Bham", "Manc", "Heath"};//array of airports declared
private String[] destination = {"","NY", "Cali", "FlO", "MIAMI", "Tokyo"};//array of destinations declared
private JPanel timePanel;
public void SelectTime() {
//combobox objects created
timePanel = new JPanel();
timePanel.setBackground(Color.BLUE);
timePanel.setPreferredSize(new Dimension(400, 400));
timeAirportbox = new JComboBox(airport);//array is inserted into the JComboBox
timePanel.add(timeAirportbox);
timeAirportbox.setVisible(false);
eastMidbox = new JComboBox(destination);
timePanel.add(eastMidbox);
eastMidbox.setVisible(false);
mancBox = new JComboBox(destination);
timePanel.add(mancBox);
mancBox.setVisible(false);
heathBox = new JComboBox(destination);
timePanel.add(heathBox);
heathBox.setVisible(false);
birmBox = new JComboBox(destination);
timePanel.add(birmBox);
birmBox.setVisible(false);
}
public JPanel getTimePanel() {
return timePanel;
}
public JComboBox getAirportBox() {
return timeAirportbox;
}
public JComboBox getEastMidBox() {
return eastMidbox;
}
public JComboBox getMancbox() {
return mancBox;
}
public JComboBox getHeathBox() {
return heathBox;
}
public JComboBox getBirmBox() {
return birmBox;
}
}
The Time object that is built in Work constructor is not used:
Time timeObject = new Time();
timeObject.SelectTime();
buttontime2 selectDest = new buttontime2();
timeObject.getAirportBox().addActionListener(selectDest);
As you are only applying the action listener selectedDest to the combobox of that timeObject, which is not used, then the listener will never be called.
You can do two things to make it work:
Move the code that creates the listener and assign it to the combox in the first listener buttontime
Create the Time object only once and store it as a member of your Work instance. As your listener is a non-static inner classes of the Work class, it will be able to use it instead of creating a new Time object.
Edit: I didn't see that in your second listener, you were AGAIN building a new Time object. This object is really a different one than the one you have created earlier, so modifying one will not affect the other. You really should create the Time object once and store it as a member variable of your Work class, and then use this object in your listeners instead of recreating it.
To be clear, do it like this:
public class Work extends JFrame {
// ...
private Time timeObject;
public Work() {
// ...
timeObject = new Time();
timeObject.SelectTime();
buttontime2 selectDest = new buttontime2();
timeObject.getAirportBox().addActionListener(selectDest);
}
public class buttontime implements ActionListener {
public void actionPerformed(ActionEvent clickTime) {
// use timeObject, don't create it and don't call SelectTime()
// example:
add(timeObject.getTimePanel(),BorderLayout.EAST);
// ....
}
}
public class buttontime2 implements ActionListener {
public void actionPerformed(ActionEvent clickTime) {
// use timeObject, don't create it and don't call SelectTime()
}
}
}
Also to note:
You should not extends JFrame, there is no reason to do so. Refactor your code so that your frame is just a member variable of your Work class.
Follow Java standard code conventions, especially use case properly with class names: buttonlistener should be ButtonListener, and method should start with lowercase: SelectTime should be selectTime.
I am using a gui with JTextFields to collect some information and then a JButton that takes that infomration and writes it to a file, sets the gui visibility to false, and then uses Runnable to create an instance of another JFrame from a different class to display a slideshow.
I would like to access some of the information for the JTextFields from the new JFrame slideshow. I have tried creating an object of the previous class with accessor methods, but the values keep coming back null (I know that I have done this correctly).
I'm worried that when the accessor methods go to check what the variables equal the JTextFields appear null to the new JFrame.
Below is the sscce that shows this problem.
package accessmain;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class AccessMain extends JFrame implements ActionListener
{
private static final int FRAMEWIDTH = 800;
private static final int FRAMEHEIGHT = 300;
private JPanel mainPanel;
private PrintWriter outputStream = null;
private JTextField subjectNumberText;
private String subjectNumberString;
public static void main(String[] args)
{
AccessMain gui = new AccessMain();
gui.setVisible(true);
}
public AccessMain()
{
super("Self Paced Slideshow");
setSize(FRAMEWIDTH, FRAMEHEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
//Begin Main Content Panel
mainPanel = new JPanel();
mainPanel.setBorder(new EmptyBorder(0,10,0,10));
mainPanel.setLayout(new GridLayout(7, 2));
mainPanel.setBackground(Color.WHITE);
add(mainPanel, BorderLayout.CENTER);
mainPanel.add(new JLabel("Subject Number: "));
subjectNumberText = new JTextField(30);
mainPanel.add(subjectNumberText);
mainPanel.add(new JLabel(""));
JButton launch = new JButton("Begin Slideshow");
launch.addActionListener(this);
mainPanel.add(launch);
//End Main Content Panel
}
#Override
public void actionPerformed(ActionEvent e)
{
String actionCommand = e.getActionCommand();
if(actionCommand.equals("Begin Slideshow"))
{
subjectNumberString = subjectNumberText.getText();
if(!(subjectNumberString.equals("")))
{
System.out.println(getSubjectNumber());
this.setVisible(false);
writeFile();
outputStream.println("Subject Number:\t" + subjectNumberString);
outputStream.close();
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
AccessClass testClass = new AccessClass();
testClass.setVisible(true);
}
});
}
else
{
//Add warning dialogue here later
}
}
}
private void writeFile()
{
try
{
outputStream = new PrintWriter(new FileOutputStream(subjectNumberString + ".txt", false));
}
catch(FileNotFoundException e)
{
System.out.println("Cannot find file " + subjectNumberString + ".txt or it could not be opened.");
System.exit(0);
}
}
public String getSubjectNumber()
{
return subjectNumberString;
}
}
And then creating a barebones class to show the loss of data:
package accessmain;
import javax.swing.*;
import java.awt.*;
public class AccessClass extends JFrame
{
AccessMain experiment = new AccessMain();
String subjectNumber = experiment.getSubjectNumber();
public AccessClass()
{
System.out.println(subjectNumber);
}
}
Hardcoding the accessor method with "test" like this:
public String getSubjectNumber()
{
return "test";
}
Running this method as below in the new JFrame:
SelfPaceMain experiment = new SelfPaceMain();
private String subjectNumber = experiment.getSubjectNumber();
System.out.println(subjectNumber);
Does cause the system to print "test". So the accessor methods seem to be working. However, trying to access the values from the JTextFields doesn't seem to work.
I would read the information from the file I create, but without being able to pass the subjectNumber (which is used as the name of the file), I can't tell the new class what file to open.
Is there a good way to pass data from JTextFields to other classes?
pass the argument 'AccessMain' or 'JTextField' to the second class:
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
AccessClass testClass = new AccessClass(AccessMain.this); //fixed this
testClass.setVisible(true);
}
});
Then reading the value of 'subjectNumber'(JTextField value) from the 'AccessMain' or 'JTextField' in the second class:
public class AccessClass extends JFrame
{
final AccessMain experiment;
public AccessClass(AccessMain experiment)
{
this.experiment = experiment;
}
public String getSubjectNumber(){
return experiment.getSubjectNumber();
}
}
Also, you should try Observer pattern.
A simple demo of Observalbe and Observer
Observable and Observer Objects