سؤال

I need some help with a simple java application which makes use of two jframe to get some input parameters. Here's a sketch of my code:

//second jframe, called when the button OK of the first frame is clicked
public class NewParamJFrame extends JFrame{
  ...
}

//first jframe
public class StartingJFrame extends JFrame{
  private static  NewParamJFrame newPFrame = null;
  private JTextField gnFilePath;
  private JButton btnOK;

  public StartingJFrame(){
            //..
    initComponents();
  }

  private void initComponents(){
     btnOK.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e){
        try{
        EventQueue.invokeAndWait(new Runnable(){
           public void run() {
               try {
               newPFrame = new NewParamJFrame();
               newPFrame.setVisible(true);
               } catch (Exception e) {
               e.printStackTrace();
               }
           }
         });
        }
        catch(InvocationTargetException e2) {} 
        catch(InterruptedException e1){}
        dispose();
      }
  }

  public String getText(){
       return gnFilePath.getText();
  }
}

public class Main {
  private static StartingJFrame begin = null;
  public static void main(String[] args) {
     try{
        EventQueue.invokeAndWait(new Runnable(){
            public void run() {
                try {
                    begin = new StartingJFrame();
                    begin.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    catch(InvocationTargetException e) {} 
    catch(InterruptedException e1){}

    String s= begin.getText();

    //...use s ...
  }
}

The call to getText() causes a NullPointerException. I want the main class to wait until the frames are closed but I don't know how to do. I'm using swing for the first time.

هل كانت مفيدة؟

المحلول

I want the main class to wait until the frames are closed but I don't know how to do. I'm using swing for the first time.

If I understand your problem correctly, you need StartingJFrame to stay waiting until NewParamJFrame is closed and then continue its execution. If this is the case then it won't happen because JFrame doesn't support modality. But JDialog does, so you can have just one JFrame and do the parameters request in a JDialog whose parent is this JFrame.

For a better explanation about modality, take a read to How to Use Modality in Dialogs.

Also take a look to this topic: The Use of Multiple JFrames, Good/Bad Practice?

In any case you'll probably face a new problem: what should the JFrame do if the user closes/cancels the dialog withouth input any parameter? How could this JFrame know what just happened in that dialog? One approach is described in this answer. You'll see the example is about a login dialog but the problem is similar to this one: How could a dialog notify to its parent frame on how the process went?

نصائح أخرى

The easiest way to wait for close without modifying the code flow is to use a modal JDialog. So you have to change your StartingJFrame class to make it a subclass of JDialog instead of JFrame, and add the following to the begin of its constructor:

super((Window)null);
setModal(true);

Then the setVisible(true); invocation on the StartingJFrame instance will wait until the dialog has been closed and hence the invokeAndWait invocation will wait too.

The call to getText() causes a NullPointerException.

Because, gnFilePath of JTextField is null.

private JTextField gnFilePath;

public String getText(){
       return gnFilePath.getText();// NullPointerException is throw here.
}

To avoid NPE, you need to initialize JTextField and JButton like below.

  private JTextField gnFilePath=new JTextField();
  private JButton btnOK=new JButton()

Try putting this:

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

public class MyWindow extends JFrame{
    MyWindow(){
        setSize(300, 200);
        setLayout(null);    
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JButton b = new JButton("Close");
        b.setBounds((300-80)/2, (200-30)/2, 80, 30);
        //
        final MyWindow frame = this;
        b.addActionListener(
            new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent ev){
                    synchronized(frame){
                        frame.notify();
                    }
                
                    frame.setVisible(false);
                    frame.dispose();
                }
            }
        );
        //
        getContentPane().add(b);
        
        setVisible(true);
        
        synchronized(this){
            try{ 
                this.wait();
            }
            catch(InterruptedException ex){ }
        }
    }


    public static void main(String args[]) {
        new MyWindow();
        System.out.println("You are here");
    }
}

The code above is checked.

Using a JDialog is probably the simplest solution, but in some cases it's desirable to have a JFrame, for example to show the window in the taskbar. Using a synchronization mechanism as suggested by Octavio is a way to achieve this, here is an alternative using a CountDownLatch blocking the main thread until the frame is closed:

public static void main(String[] args) throws Exception {
    CountDownLatch latch = new CountDownLatch(1);

    SwingUtilities.invokeLater(() -> {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.setSize(300, 200);
        frame.setVisible(true);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosed(WindowEvent e) {
                latch.countDown();
            }
        });
    });

    latch.await();
    System.out.println("Main thread released");
}

You can use a loop (preferably do-while loop) to put a frame on hold until the other frame closes or hides. Make sure to break the loop or increment the variable used for the loop by specific amount when the other frame is disposed or hidden. This way you can keep your StartingJFrame class to remain as a subclass of JFrame.

    do {
        if (changeLog.isVisible()) {
        } else {
            changeLog.dispose();
            break;
        }
    } while (hold < 1);

or

    do {
        if (changeLog.isActive()) {
        } else {
            break;
        }
    } while (hold < 1);

The first one would require the previous frame to be hidden (JFrame.HIDE_ON_EXIT or Window.setVisible(false)) before the codes can be run. The last one would require the previous frame to be "disposed" (JFrame.DISPOSE_ON_EXIT or (subclass of JFrame).dispose(). Add any of those codes on StartingJFrame, though, since you created a NewParamJFrame in that class and have the corresponding field(s) set to private.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top