0

I've been working with JFrame recently and had a simple login, register and popup frames work when I had them in a single class. I wanted to make it nicer and not all packed inside one class so I made a class for the frames, buttons, panels, variables and the main class. My problem is that The frames themselves are working fine and loading up and displaying, but the ActionListeners on the buttons aren't working at all. Nothing changes when I hit a button etc. I'm fairly new to Java and very new to the JFrames and JButtons. Is there anything I can be doing to make this simpler or make my code look better? The code will be in each seperate class:

Right now nothing is running, even the "This is running" in main before it's supposed to call LoginScreen() doesn't run. I'm not sure what I changed to make this happen?

Main Class:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;

public class myGame {


   public static void main(String[] args){ 
      buttons myButtons = new buttons();
      frames myFrames = new frames();
      panels myPanels = new panels();
      variables myVariables = new variables();    

      System.out.println("This is running");  
      myFrames.loginScreenFrame();     
      System.out.println("This is also running");                                                                      }
}

frames class:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;

public class frames{


   public JFrame loginScreenFrame(){
      variables myVariables = new variables();
      panels myPanels = new panels();
      buttons myButtons = new buttons();


      myVariables.loginFrame.setSize(300,125);
      myVariables.loginFrame.setLocation(550,250);

      myVariables.loginFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);

      Container content = myVariables.loginFrame.getContentPane();
      content.add(myPanels.loginScreenPanel(), BorderLayout.CENTER);
      content.add(myPanels.loginScreenButtonsPanel(), BorderLayout.SOUTH);
      myButtons.registerButton.addActionListener(myButtons.registerListener);

      myVariables.loginFrame.setVisible(true);

      return myVariables.loginFrame;
   }

   public JFrame registerFrame(){

      variables myVariables = new variables();
      panels myPanels = new panels();

      myVariables.registerFrame.setSize(400,125);
      myVariables.registerFrame.setLocation(550,250);
      myVariables.registerFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      Container content = myVariables.registerFrame.getContentPane();

      content.add(myPanels.registerScreenPanel(), BorderLayout.CENTER);
      content.add(myPanels.registerScreenButtonsPanel(), BorderLayout.SOUTH);

      myVariables.registerFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);

      myVariables.registerFrame.setVisible(true); 

      return myVariables.registerFrame;
   }
}

Buttons Class:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;

public class buttons{
   JButton loginButton = new JButton("Login");
   JButton registerButton = new JButton("Register");
   JButton cancelButton = new JButton("Cancel");
   JButton checkUsernameButton = new JButton("Check Username");


   public void actionListeners(){

   variables myVariables = new variables();
   frames myFrames = new frames();
   panels myPanels = new panels();

      ActionListener cancelListener = new ActionListener(){
         public void actionPerformed(ActionEvent ae){
            frames myFrames = new frames();
            myFrames.registerFrame().dispose();
         }
      };

      ActionListener usernameListener = new ActionListener(){
         public void actionPerformed(ActionEvent ae){

         }
      };

      ActionListener passwordListener = new ActionListener(){
         public void actionPerformed(ActionEvent ae){

         }
      };

      ActionListener passwordCheckListener = new ActionListener(){
         public void actionPerformed(ActionEvent ae){

         }
      };

      ActionListener checkUsernameListener = new ActionListener(){
         public void actionPerformed(ActionEvent ae){
            variables myVariables = new variables();
            if (myVariables.usernameAndPassword.get(myVariables.username) == null){
               JPanel okButtonPanel = new JPanel();
               final JFrame invalidUsernameFrame = new JFrame();
               invalidUsernameFrame.setSize(400,75);
               invalidUsernameFrame.setLocation(550,250);
               JButton okButton = new JButton("Ok");
               Container invalidUsernameContainer =  invalidUsernameFrame.getContentPane();
               okButtonPanel.add(okButton);
               JLabel invalidUsernameLabel = new JLabel();
               invalidUsernameLabel.setText("                            Username is Available!");
               invalidUsernameContainer.add(invalidUsernameLabel);
               invalidUsernameContainer.add(okButtonPanel, BorderLayout.SOUTH);
               invalidUsernameFrame.setVisible(true);
               ActionListener okListener = new ActionListener(){
                  public void actionPerformed(ActionEvent ae){
                     myGame mainClass = new myGame();
                     invalidUsernameFrame.dispose();
                  }
               };
               okButton.addActionListener(okListener);
            }

            else{
               JPanel okButtonPanel = new JPanel();
               final JFrame invalidUsernameFrame = new JFrame();
               invalidUsernameFrame.setSize(400,75);
               invalidUsernameFrame.setLocation(550,250);
               JButton okButton = new JButton("Ok");
               Container invalidUsernameContainer = invalidUsernameFrame.getContentPane();
               okButtonPanel.add(okButton);
               JLabel invalidUsernameLabel = new JLabel();
               invalidUsernameLabel.setText("                         Username is not Available");
               invalidUsernameContainer.add(invalidUsernameLabel);
               invalidUsernameContainer.add(okButtonPanel, BorderLayout.SOUTH);
               invalidUsernameFrame.setVisible(true);
               ActionListener okListener = new ActionListener(){
                  public void actionPerformed(ActionEvent ae){
                     myGame mainClass = new myGame();
                     invalidUsernameFrame.dispose();
                  }
               };
               okButton.addActionListener(okListener);
            }
         }
      };
      ActionListener registerListener = new ActionListener(){
         public void actionPerformed(ActionEvent ae){
            frames myFrames = new frames();
            myFrames.registerFrame();
         }
      };
      cancelButton.addActionListener(cancelListener);
      myVariables.usernameField.addActionListener(usernameListener);
      myVariables.passwordField.addActionListener(passwordListener);
      myVariables.passwordCheckField.addActionListener(passwordCheckListener);
      registerButton.addActionListener(registerListener);

      checkUsernameButton.addActionListener(checkUsernameListener);
   }


}

Panels Class:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class panels{


      buttons myButtons = new buttons();
      frames myFrames = new frames();
      variables myVariables = new variables();

   public JPanel loginScreenPanel(){

      buttons myButtons = new buttons();
      frames myFrames = new frames();
      variables myVariables = new variables();

      JPanel panel = new JPanel();
      panel.setLayout(new GridLayout(2,2));    
      panel.add(new JLabel("Username:"));  
      panel.add(myVariables.usernameFrame);
      panel.add(new JLabel("Password:"));
      panel.add(myVariables.passwordFrame);

      return panel;
   }

   public JPanel loginScreenButtonsPanel(){


      JPanel buttons = new JPanel();
      buttons.add(myButtons.loginButton);
      buttons.add(myButtons.registerButton);
      buttons.add(myButtons.cancelButton);


      return buttons;
   }

   public JPanel registerScreenPanel(){



      JPanel panel = new JPanel();
      panel.setLayout(new GridLayout(3,2));    
      panel.add(new JLabel("Username:"));  
      panel.add(myVariables.usernameField);
      panel.add(new JLabel("Password:"));
      panel.add(myVariables.passwordField);
      panel.add(new JLabel("Re-Enter Password:"));
      panel.add(myVariables.passwordCheckField);


      return panel;
   }

   public JPanel registerScreenButtonsPanel(){


      JPanel buttons = new JPanel();
      buttons.add(myButtons.registerButton);
      buttons.add(myButtons.checkUsernameButton);
      buttons.add(myButtons.cancelButton);


      return buttons;
   }
}

New Code:

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ExampleGame {

java.util.HashMap<String,char[]> usernamesAndPasswords = new         java.util.HashMap<String,char[]>();
public static void main(String[] args) {
    new ExampleGame();
}

public ExampleGame() {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            }

            LoginPane pane = new LoginPane();
            storeInfo info = new storeInfo();
            int result = JOptionPane.showOptionDialog(null, pane, "Login", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{"Login", "Cancel"}, 0);
            if (result == 0) {

                User user = pane.getUser();
                // Perform the login...


                usernamesAndPasswords = info.storeInfo(user.name, user.password, usernamesAndPasswords);
                System.out.println("Name entered: " + user.name);
                System.out.print("Password entered: ");
                System.out.println(user.password);
                System.out.println(usernamesAndPasswords.get(user.name));

            }

        }
    });
}

public class LoginPane extends JPanel {

    private JTextField userName;
    private JPasswordField password;

    public LoginPane() {

        userName = new JTextField(10);
        password = new JPasswordField(10);

        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(4, 4, 4, 4);
        gbc.anchor = GridBagConstraints.EAST;
        add(new JLabel("Username:"), gbc);

        gbc.gridx++;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(userName, gbc);

        gbc.gridx = 0;
        gbc.gridy++;
        gbc.fill = GridBagConstraints.NONE;
        add(new JLabel("Password:"), gbc);

        gbc.gridx++;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(password, gbc);

    }

    public User getUser() {

        return new User(userName.getText(), password.getPassword());

    }

}

public class User {

    private String name;
    private char[] password;

    public User(String name, char[] password) {
        this.name = name;
        this.password = password;
        }

    }

    public class storeInfo{

      public java.util.HashMap storeInfo(String name, char[] password, java.util.HashMap <String, char[]> usernamesAndPasswords){

         usernamesAndPasswords.put(name, password);
         return usernamesAndPasswords;
      }

    }

}

I added a class to your example to get it to store the values in a HashMap, I'm wondering if I did this right or if there is some other better way to do it? Right now it does store the values in a HashMap, but it gives me a warning: Note: ExampleGame.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.

I read up on the links you gave, some of it didn't make sense but I think for the most part it did. just before I start going to try and get this to work I want to make sure I did that HashMap right.

Also do I have your permission to use the code you made? I could make something similar myself but it probably wouldn't be as nice and clean.

Molten
  • 27
  • 1
  • 9
  • You keep creating instances of variables which have no relationship to each other. For example, in your close action listener, you create another instance of `frames`, which is not the instance which is on the screen. – MadProgrammer Oct 29 '13 at 00:21
  • do you mean the cancel listener? where I have: frames myFrames = new frames()? Here I'm just calling the one I already made. I'm not really sure what you mean. Can you be more specific? – Molten Oct 29 '13 at 02:37
  • In your `buttons` class, in the `actionListeners` you create 3 new instances of `myFrames` of which none of them actually have any context to what is actually on the screen. It total you do this 6 times... – MadProgrammer Oct 29 '13 at 02:49
  • So how do I call my frames then? When I don't have those it says buttons.java:97: error: local variable myFrames is accessed from within inner class; needs to be declared final, other than that without the myFrames.loginScreenFrame, it says error cannot find symbol. – Molten Oct 29 '13 at 03:58
  • This will depend a lot on your application, but I would imagine you would pass a reference to all the objects you need either via the class constructor or some kind of setter methods and maintain these references as instance variables, making them available to the whole class – MadProgrammer Oct 29 '13 at 04:10
  • How would I do that for this instance? I'm new to JFrame and have no idea how i'd do that with action listeners etc. – Molten Oct 29 '13 at 05:31
  • I added some more info to the end of the main thing with the changed buttons file, now when I run it, nothing shows up and none of the screens are showing up. – Molten Oct 29 '13 at 05:42
  • Basically, you code is a bowl of spaghetti and untangling in it's current form might not be possible. – MadProgrammer Oct 29 '13 at 05:46
  • How do I fix this? I tried to make it flow in the best way possible but I guess that didn't work out to well. If I were to go about to make this so it's more readable and works, how would I do that? Not really sure why it's not running either since it should be doing something before it calls loginScreen() – Molten Oct 29 '13 at 05:50
  • I literally just swapped two lines of code and now I'm not getting a stack overflow error, however now I'm still getting my first problem is that my buttons aren't doing anything when they are pressed. – Molten Oct 29 '13 at 06:06

1 Answers1

0

Basically, you have a tightly coupled bowl of spaghetti which in it's current state, probably can't be untangled.

The main problem you have is the fact that you are creating new instance of frames, panels, buttons and variables all over the place. This means that from one part of your application to the next, they are all using their own instance of these classes, which don't relate to each other.

The other problem is that many of these parts of the application actually shouldn't need direct access to the objects that you are try to access.

Instead, start by breaking down you application into usable components/elements, with defined responsibilities.

For example, the UI parts should gather information which is feed back into the system for processing. The processing layer shouldn't care where this information came from, only that it conforms to it's needs.

For example...

The following simply generates a login dialog which will return a User object which represents the username and password that the user typed in (this is a very basic example, so validation is light)

The login dialog does not care about how the login process occurs, only it is only responsible for getting the information

Equally, no other part of the application needs access to the fields within the LoginPane, they should only care about the result

enter image description here

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ExampleGame {

    public static void main(String[] args) {
        new ExampleGame();
    }

    public ExampleGame() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                LoginPane pane = new LoginPane();
                int result = JOptionPane.showOptionDialog(null, pane, "Login", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{"Login", "Cancel"}, 0);
                if (result == 0) {

                    User user = pane.getUser();
                    // Perform the login...

                }

            }
        });
    }

    public class LoginPane extends JPanel {

        private JTextField userName;
        private JPasswordField password;

        public LoginPane() {

            userName = new JTextField(10);
            password = new JPasswordField(10);

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.insets = new Insets(4, 4, 4, 4);
            gbc.anchor = GridBagConstraints.EAST;
            add(new JLabel("Username:"), gbc);

            gbc.gridx++;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add(userName, gbc);

            gbc.gridx = 0;
            gbc.gridy++;
            gbc.fill = GridBagConstraints.NONE;
            add(new JLabel("Password:"), gbc);

            gbc.gridx++;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add(password, gbc);

        }

        public User getUser() {

            return new User(userName.getText(), password.getPassword());

        }

    }

    public class User {

        private String name;
        private char[] password;

        public User(String name, char[] password) {
            this.name = name;
            this.password = password;
        }

    }

}

You should take a look at the Model-View-Control pattern

Program to interface

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for the response, I just think what I know about JFrame etc isn't enough to do this correctly, I'm not 100% on Grid bag but I get the jist. Also for public User getUser, are you basically returning the public User that's in public class User? I didn't really know you could do that. Also not really sure what UIManager is either.. Any suggestions on where I should go to learn these kinds of things? Also what does this line do? int result = JOptionPane.showOptionDialog(null, pane, "Login", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{"Login", "Cancel"}, 0); – Molten Oct 29 '13 at 06:31
  • `JOptionPane.showOptionDialog` creates the dialog and dialog buttons – MadProgrammer Oct 29 '13 at 06:32
  • You could take a look at [Creating an GUI with Swing](http://docs.oracle.com/javase/tutorial/uiswing/), but I would also suggest you spend some time with [Learning the Java Language](http://docs.oracle.com/javase/tutorial/java/index.html) – MadProgrammer Oct 29 '13 at 06:34
  • I'd up vote your answer but I can't without 15 reputation I guess. What does the @Override do exactly? I've never seen that before. And the EventQueue.invokeLater(new Runnable()? Not really sure what that is. Also I noticed you never used JFrame why is that? Not really sure what the gbc.insets or gbc.anchor does. Is the gridx++, gridy++ gridx = 0, so it gets (pos1,pos2 (new line) pos 3, pos4) – Molten Oct 29 '13 at 06:35
  • I know the basics of Java + a bit more, and I have experience in object oriented programming I'm just not familiar at all with GUIs or the Java syntax for certain things. Thanks for the links though I'll check them out. I have two books on Java and one is where I was doing some GUI things from which didn't go over actionListeners and buttons etc very well. – Molten Oct 29 '13 at 06:38
  • `@Override` is an [annotation](http://docs.oracle.com/javase/tutorial/java/annotations/) use by the compiler to ensure that I've actually overridden the method I thought I was. `EventQueue.invokeLater` ensures that the code within the `run` method of the specified `Runnable` is executed within the context of the [Event Dispatching Thread](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html). I don't use a `JFrame` as I rely on the [`JOptionPane`](http://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html) to make a dialog for me – MadProgrammer Oct 29 '13 at 06:38
  • The main reason for using `JOptionPane` is it will stop the program execution until the dialog is dismissed. `gbc` are [`GridBagConstraints`](http://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html) which change how `GridBagLayout` lays out it's components, and yes, `gridx` and `gridy` are defining the cells into which components will be placed – MadProgrammer Oct 29 '13 at 06:40
  • Thanks again for all the replies, I read up a bit on what you linked and I think it makes more sense now but still not all the way there. I added a HashMap and a method that store into the HashMap the way I think you did it for the rest of the methods. Can you look that over for me and let me know? The new code is the last set in the original question. Also do I have your permission to use the code you posted or do you want me to make my own? I could but it just wouldn't be as nice and as clean. Also, I'd like to move this to a discussion chat but I only have 1 reputation so I can't, – Molten Oct 29 '13 at 20:07
  • @user2045177 For what it's worth, you can use my code all you like ;) – MadProgrammer Oct 29 '13 at 20:19
  • Awesome thanks =), now I'm just wondering if my implementation of my HashMap alright or not, and if I can ignore the little warning message I'm getting. – Molten Oct 29 '13 at 20:47
  • private char[] password; why do you have it with an array of characters? Wouldn't it be easier to just use a string? – Molten Oct 29 '13 at 21:15
  • @Molten `String` is interned for the life span of the JVM, meaning that it is possible to for someone to inspect the memory and find the password. See [this](http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords) and [this](http://javarevisited.blogspot.com.au/2012/03/why-character-array-is-better-than.html) and [this](http://securesoftware.blogspot.com.au/2009/01/java-security-why-not-to-use-string.html) for more details – MadProgrammer Oct 29 '13 at 21:55
  • Using a `HashMap` isn't necessarily a bad idea, the problem you have is restricting access to it. How do you stop other parts of your program from changing it? – MadProgrammer Oct 29 '13 at 21:58
  • I don't really mind someone finding the password at this point, it is going to be a very simple thing that will probably have cheats in it anyway, the only reason I have the login is for the saves essentially. And what do you mean by your second question? If what it's what I think you mean wouldn't it just be private? I just want to be able to add users and passwords to the HashMap, when adding those then send them to a file. At the start of the program load the file into the HashMap. And at login time check the HashMap values. So the only time I need to change it is during – Molten Oct 30 '13 at 00:27
  • Read from the files, and when I get new input, if I set it to private wouldn't those have to be in the same class? Or is there a different way to do that? – Molten Oct 30 '13 at 00:28
  • Then I suggest you take a look at the `Properties` or `Preferences` API which does this job for you. This is just my opinion, but exposing values in this way and removing from there controlling context is, generally, a bad design – MadProgrammer Oct 30 '13 at 00:45