在 ActionListener 内部调用时 JFrame 不绘制内容

JFrame does not draw content when called inside ActionListener

我正在尝试制作一组​​ 2 个 GUI:一个,当单击一个按钮时,调用另一个,根据在第二个 GUI 中单击哪个按钮,returns 第一个的值图形用户界面。不幸的是,当从第一个 GUI 的 actionPerformed 方法调用时,第二个 GUI 显示为空白。但是,使用 JOptionPane 时不会发生这种情况。

JOptionPane 做了什么使其可以在 actionPerformed 方法内工作,为什么我的示例代码不能在 actionPerformed 方法内工作?

第一个GUI调用第二个GUI的代码如下:

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();
}
} 

第二个GUI的代码如下:

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());
}
}

此代码完全 运行 可以使用我发布的两个 类 和适当的导入语句。第二个 GUI 也可以 运行 独立显示从第一个 GUI 调用时应该出现的内容,第一个 GUI 包含一个正常工作的示例 JOptionPane

请帮我弄清楚为什么 actionPerformed 方法只阻止某些 GUI 渲染,而其他 GUI 正常工作!

您正在阻止美国东部时间! actionPerformed 在 EDT 上执行,所以 getSelectedIndex 也是,wait.await() 阻止它。请注意,一旦发生这种情况,第一帧也不会响应(并且最小化和取消最小化帧甚至不会绘制它们)。即使显示第二帧,它也不会响应用户交互,因为第一个 actionPerformed 没有 return.

我不明白你为什么需要 CountDownLatchgetSelectedIndex 只能在按下 confrimBtn 后执行,因此此时只需 return 选定的按钮。这不是唯一的解决方案 - 您的设计最终将决定 类.

之间的交互

SelectionDialogactionPerformed中写:

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

并删除 getSelectedIndex 方法。

OpeningGUI中添加如下方法

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

    System.out.println(list);
}

并从其 actionPerformed 中删除对 getSelectedIndex 的调用。

备注:

  • 您可以使用 setLocationRelativeTo(null).
  • 而不是 setLocation 的屏幕尺寸计算
  • 在调用 pack 时调用 setSize 会使第一个调用变得多余。
  • 无需在右侧指定泛型:

    selectionIndecies = new ArrayList<>();
    
  • Swing 应该在 EDT 开始(参见 here)。

  • 您可能会使用对话框而不是另一个对话框做得更好 JFrame
  • 对功能不同的按钮使用不同的 ActionListener,而不是在每次调用时检查来源。