简体   繁体   中英

JFrame does not draw content when called inside ActionListener

I am trying to make a set of 2 GUIs: one, when a button is clicked, calls another, which, based on which button is clicked in the second GUI, returns a value to the first GUI. Unfortunately, when called from the first GUI's actionPerformed method, the second GUI appears blank. This does not, however, happen when JOptionPane is used.

What does JOptionPane do that allows it to work inside the actionPerformed method, and why does my example code not work inside the actionPerformed method?

The code for the first GUI, which calls the second GUI, is as follows:

public class OpeningGUI extends JFrame implements ActionListener {

private static final long serialVersionUID = 1L;

private Container container;
private JButton btn, btn2;

/**
 * Constructor for class OpeningGUI - establish the JFrame
 * Loads the window and moves it to the center of the screen.
 */
public OpeningGUI() {
    // when mama ain't happy, ain't nobody happy
    super("Dominion Launcher");

    //UI components get established here
    container = getContentPane();   // Container is the abstract concept of the area inside a window
    container.setLayout(new BorderLayout());
    container.add(getCenterPanel(), BorderLayout.CENTER);
    setSize(700, 300);
    pack();
    setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/2 - this.getWidth()/2, 
            (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/2 - this.getHeight()/2);
    setVisible(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
}

/**
 * Sets the game mode based on which button is clicked.
 * Click stops return method from waiting.
 */
public void actionPerformed(ActionEvent e) {
    if(e.getSource() == btn)    {   
        SelectionDialog sd = new SelectionDialog("Select up to 3 buttons, then click done when selection complete", 3);
        System.out.println(sd.getSelectedIndex());
    }
    if(e.getSource() == btn2)   {   
        JOptionPane.showConfirmDialog(null, "See it works, right");
    }
}

/**
 * Sets up the center panel with buttons to select game mode.
 * @return the center panel.
 */
public JPanel getCenterPanel()  {
    JPanel temp = new JPanel();
    btn = new JButton("SelectionDialog tester");
    temp.add(btn);
    btn.addActionListener(this);
    btn2 = new JButton("JOptionPane tester");
    temp.add(btn2);
    btn2.addActionListener(this);
    return temp;
}

/**
 * Main method of OpeningGUI.  Used to run the program.
 * @param args command-line arguments. Unused.
 */
public static void main(String[] args) {
    new OpeningGUI();
}
} 

The code for the second GUI is as follows:

public class SelectionDialog extends JFrame implements ActionListener {

private static final long serialVersionUID = 1L;

private Container container;
private JButton confirmBtn;
private JButton[] buttons;
private ArrayList<Integer> selectionIndecies;
private CountDownLatch wait;
private String message;
private int numNeeded;
private boolean isMaximum;


/**
 * Constructor for the SelectionDialog class.  
 * Selects from an ArrayList of buttons.
 * @param message Message to display.
 * @param num number to select.
 */
public SelectionDialog(String message, int num) {
    super("Please Select Buttons");

    this.message = message;
    numNeeded = num;
    isMaximum = false;

    setupUI();
}

/**
 * Establishes the JFrame and sets values for some fields.
 */
private void setupUI() {
    selectionIndecies = new ArrayList<Integer>();

    wait = new CountDownLatch(1);

    //UI components get established here
    container = getContentPane();   // Container is the abstract concept of the area inside a window
    container.setLayout(new BorderLayout());
    container.add(getTopPanel(), BorderLayout.NORTH);
    container.add(getCenterPanel());
    pack();
    setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/2 - this.getWidth()/2, 
            (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/2 - this.getHeight()/2);
    setVisible(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

}   

/**
 * Changes color of buttons and adds or removes them from the selected arrays.
 */
public void actionPerformed(ActionEvent e) {
    if(e.getSource() == confirmBtn) {   
        if((!isMaximum && selectionIndecies.size() <= numNeeded) 
                || selectionIndecies.size() == numNeeded) {
            wait.countDown();
            dispose();
        }
    }
    for(int i = 0; i < buttons.length; i++) {
        if(e.getSource() == buttons[i]) {
            if(!buttons[i].getBackground().equals(Color.ORANGE)) {
                buttons[i].setBackground(Color.ORANGE);
                buttons[i].setBorderPainted(false);
                selectionIndecies.add(new Integer(i));
                repaint();
            }
            else {
                buttons[i].setBackground(Color.LIGHT_GRAY);
                selectionIndecies.remove(new Integer(i));
                repaint();
            }
        }
    }
}

/**
 * Creates the top panel of the GUI. 
 * Contains the prosperity check box, the number of players selector, 
 * and the card counter and confirm button.
 * @return the top panel.
 */
private JPanel getTopPanel()    {
    JPanel topPanel = new JPanel();
    JLabel temp = new JLabel(message + "       ");
    topPanel.add(temp);
    confirmBtn = new JButton("Done");
    topPanel.add(confirmBtn);
    confirmBtn.addActionListener(this);
    return topPanel;
}

/**
 * Determines which buttons were selected.
 * Waits until Ok has been clicked and a proper number of buttons had been selected.
 * @return an array of indecies of the buttons selected.
 */
public ArrayList<Integer> getSelectedIndex() {
    try {
        wait.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Collections.sort(selectionIndecies); 
    return selectionIndecies;
}

/**
 * Sets up center panel with ArrayList of buttons, 
 * and panels of buttons.
 */
private JScrollPane getCenterPanel()    {
    JPanel centerPanel = new JPanel();
    buttons = new JButton[6];
    for(int i = 0; i < 6; i++) {
        JButton temp = new JButton("Button " + i);
        temp.addActionListener(this);
        temp.setVisible(true);
        centerPanel.add(temp);
        buttons[i] = temp;
    }
    return new JScrollPane(centerPanel);
}

/**
 * Main method of the SelectionDialog class.  For testing only.
 * @param args command line arguments.  Unused.
 */
public static void main(String[] args) {
    SelectionDialog sd = new SelectionDialog("Select up to 3 buttons, then click done when selection complete", 3);
    System.out.println(sd.getSelectedIndex());
}
}

This code is completely runnable with the two classes I have posted and appropriate import statements. The second GUI can also be run independently to show what should appear when called from the first GUI, and the first GUI contains an example JOptionPane that works normally.

Please help me figure out why the actionPerformed method stops only some GUIs from rendering, while others work normally!

You're blocking the EDT! actionPerformed is executed on the EDT, so getSelectedIndex is also and wait.await() blocks it. Notice that once this happens, the first frame doesn't respond also (and minimizing and un-minimizing the frames will not even paint them). Even if the 2nd frame were to show, it would not respond to user interaction because the first actionPerformed did not return.

I don't understand why you need the CountDownLatch . getSelectedIndex can only execute once confrimBtn is pressed, so just return the selected buttons at that point. This isn't the only solution - your design will eventually dictate the interaction between the classes.

In SelectionDialog 's actionPerformed write:

if (e.getSource() == confirmBtn) {
    if ((!isMaximum && selectionIndecies.size() <= numNeeded) || selectionIndecies.size() == numNeeded) {
        Collections.sort(selectionIndecies);
        OpeningGUI.publishSelectedIndex(selectionIndecies);
        dispose();
    }
}

and remove the getSelectedIndex method.

In OpeningGUI , add the following method

public static void publishSelectedIndex(ArrayList<Integer> list) {

    System.out.println(list);
}

and remove from its actionPerformed the call to getSelectedIndex .

Notes:

  • Instead of the screen size calculation for setLocation , you can use setLocationRelativeTo(null) .
  • Calling setSize when you call pack right after it makes the first call redundant.
  • No need to specify the generic type on the right-hand-side:

     selectionIndecies = new ArrayList<>(); 
  • Swing should be started on the EDT (see here ).

  • You would probably do better with a dialog instead of another JFrame .
  • Use different ActionListener s for buttons that function differently instead of checking the source with each call.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM