2

So, I started studying java about a week ago, I'm running into a few issues with a little program I'm building to train with swing and oop/java in general.

The program (so far) has a MainClass and a Window class. The MainClass creates an instance of the Window class, which creates a JFrame and saves the user input in a field .

At this point, MainClass prints the output, which I get through getters methods.

The problem is that I still think in a procedural way: MainClass prints null, because it doesn't wait for the istance of window to get user input.

How can I fix it, thus getting main to wait for the istance of window to accept user input, before printing?

Nb. The Jframe stuff works, the window appears, it's just that MainClass doesn't wait for it to do what it's supposed to. I could (I think?) use some sleep command to wait but it seems utterly wrong.

here's the code of MainClass.java

import java.util.Arrays;

public class MainClass {

    private char[] password;
    private String pin;

    public static void main(String[] args) {

        Window w = new Window();    
        System.out.println(w.getPin() + Arrays.toString(w.getPassword()) + '1');
    }


}

and Window.java

import java.awt.*;
import javax.swing.*;
import java.awt.Window.Type;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

import net.miginfocom.swing.MigLayout;


public class Window extends JFrame{

    private JTextField textField_1;
    private JButton btnNewButton;
    private JPanel panel;
    private JPasswordField passwordField;
    private char[] password = new char[10];
    private String pin;


    public Window() {

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        this.setSize(370, 150);
        this.setForeground(new Color(192, 192, 192));
        this.setTitle("Access Password Manager");
        this.setResizable(false);

        panel = new JPanel();
        getContentPane().add(panel, BorderLayout.CENTER);
        panel.setLayout(new MigLayout("", "[70.00][132.00,grow][44.00][67.00,grow][61.00][]", "[19.00][34.00][]"));

        JLabel lblNewLabel = new JLabel("Password");
        panel.add(lblNewLabel, "cell 0 1,alignx trailing,aligny center");

        passwordField = new JPasswordField();
        passwordField.setColumns(13);
        panel.add(passwordField, "cell 1 1,alignx center");

        JLabel lblNewLabel_1 = new JLabel("Key");
        panel.add(lblNewLabel_1, "cell 2 1,alignx center,aligny center");

        textField_1 = new JTextField();
        panel.add(textField_1, "cell 3 1,alignx left,aligny center");
        textField_1.setColumns(4);

        btnNewButton = new JButton("Log In");
        ListenForButton listener = new ListenForButton();

        btnNewButton.addActionListener(listener);
        panel.add(btnNewButton, "cell 4 1");

        this.setVisible(true);

    }

        private class ListenForButton implements ActionListener{


            @Override
            public void actionPerformed(ActionEvent e) {

                if(e.getSource() == btnNewButton){

                    if (passwordField.getPassword().length < 10){

                        password = passwordField.getPassword().clone();   
                    }

                    pin = textField_1.getText();

                }
            }       
        }


        public char[] getPassword(){            
            return password;            
        }

        public String getPin(){
            return pin;     
        }


}

EDIT:

It's not just about printing, which I know I could do directly into Window.class. I'm sorry if I explained myself poorly. Please consider the println as a "I need to access and work on those fields once window has saved them form the input".

Athamas
  • 609
  • 5
  • 16

3 Answers3

3

You could use a modal dialog to get user input, the dialog will block the code execution at the point it is made visible and continue when it's made invisible (it's magic), have a look at How to Make Dialogs for more details

Updated

The modal dialog will only block the Event Dispatching Thread (technically it doesn't block it, it simply circumvents it), see Initial Threads for more details

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import net.miginfocom.swing.MigLayout;

public class MainClass {

    private char[] password;
    private String pin;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                System.out.println("Before Window");
                Window w = new Window();
                System.out.println("After Window");
                System.out.println(w.getPin() + Arrays.toString(w.getPassword()) + '1');
            }
        });
    }

    public static class Window extends JDialog {

        private JTextField textField_1;
        private JButton btnNewButton;
        private JPanel panel;
        private JPasswordField passwordField;
        private char[] password = new char[10];
        private String pin;

        public Window() {

            this.setModal(true);
            this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            this.setLocationRelativeTo(null);
            this.setSize(370, 150);
            this.setForeground(new Color(192, 192, 192));
            this.setTitle("Access Password Manager");
            this.setResizable(false);

            panel = new JPanel();
            getContentPane().add(panel, BorderLayout.CENTER);
            panel.setLayout(new MigLayout("", "[70.00][132.00,grow][44.00][67.00,grow][61.00][]", "[19.00][34.00][]"));

            JLabel lblNewLabel = new JLabel("Password");
            panel.add(lblNewLabel, "cell 0 1,alignx trailing,aligny center");

            passwordField = new JPasswordField();
            passwordField.setColumns(13);
            panel.add(passwordField, "cell 1 1,alignx center");

            JLabel lblNewLabel_1 = new JLabel("Key");
            panel.add(lblNewLabel_1, "cell 2 1,alignx center,aligny center");

            textField_1 = new JTextField();
            panel.add(textField_1, "cell 3 1,alignx left,aligny center");
            textField_1.setColumns(4);

            btnNewButton = new JButton("Log In");
            ListenForButton listener = new ListenForButton();

            btnNewButton.addActionListener(listener);
            panel.add(btnNewButton, "cell 4 1");

            this.setVisible(true);

        }

        private class ListenForButton implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {

                if (e.getSource() == btnNewButton) {

                    if (passwordField.getPassword().length < 10) {

                        password = passwordField.getPassword().clone();
                    }

                    pin = textField_1.getText();

                }
            }
        }

        public char[] getPassword() {
            return password;
        }

        public String getPin() {
            return pin;
        }

    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • It doesn't look like the code execution is stopping when the dialog is made visible. – Athamas Mar 17 '16 at 20:46
  • It does, but it will only block the Event Dispatching Thread, so you need to start you code (which you should do anyway) inside the context of the Event Dispatching Thread – MadProgrammer Mar 17 '16 at 20:56
0

you should be using events. your main class could listen for an event, if the button in the window is pressed. e.g.

public static void main(String[] args) {
   Window w = new Window();  
   w.getBtnNewButton().addActionListener(new ActionListener(){
      @Override
      public void actionPerformed(ActionEvent e) {
         System.out.println(w.getPin() + Arrays.toString(w.getPassword()) +  '1');
      }
   });  
}

in this case you have to make a public getter for the button, so you can access it from the main class.

Christian
  • 51
  • 5
  • Thank you very much for the answer, I actually did not think about that ! However, I don't know about moving the listener to MainClass, wouldn't that basically be breaking the whole "oop should represent reality as much as possible"? At that point I might as well make everything in the same class, which by all means I'll do if it's the only solution, I just wonder if there's a way that goes along with oop logic. – Athamas Mar 17 '16 at 20:02
  • you have application logic which is encapsulated in its own class (your window), which is fine for now. but if this component has something of value for other classes then it can provider public getters and events which other components can listen too. But you are right, it would be better if you're not listening to the button itself (which should be private, you are right). You could make your own event (e.g. onUserOnputDone which is fired by your window if the user presses the button). Other classes then can listen to this event. – Christian Mar 17 '16 at 20:17
0

If you want the result to be printed on button click, then you could print it in the button's action listener. If you want it to be printed as soon as user enters anything, you can add action listeners to your password text field.

Window / View

public class Window extends JFrame{

  private Controller controller;
  private Model model;

  public Window(Controller controller, Model model) {
    this.controller = controller;
    this.model = model;
  }

Model

public class Model {

  private String password;
  //getters and setters
}

Controller

public class Controller {

  public void doSomething() {
    // do anything. Views could invoke controller methods to do things and is usually invoked when certain events happen.
  }

Main

public class Main {

  public static void main(String[] args) {
    new Window(new Controller(), new Model());
  }
Sunny
  • 21
  • 6
  • Thank you for your answer and time, I explained myself poorly, please see the **EDIT**. – Athamas Mar 17 '16 at 20:05
  • Ah, gotcha. In a perfect MVC model I would suggest you to create a Model class, for ex: Credentials.java with parameters userId and password. You can create the model in Window constructor. And add a getter in Window to get the model. On each actions, you should set the value to your model. Your View needs a controller class too. Then from view you can invoke controller methods. – Sunny Mar 17 '16 at 20:12
  • Added example code. Instead of thinking like "how can I wait until this event happens", it should be thought as "when this event happens, this needs to be done". This is achieved by adding the appropriate listeners and invoking methods when the expected event happens. Listeners are going to wait for that event. – Sunny Mar 17 '16 at 20:39