简体   繁体   English

在ActionListener中调用时,JFrame不绘制内容

[英]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. 我正在尝试制作2个GUI的集合:一个,当单击一个按钮时,调用另一个,这基于在第二个GUI中单击哪个按钮,将值返回给第一个GUI。 Unfortunately, when called from the first GUI's actionPerformed method, the second GUI appears blank. 不幸的是,当从第一个GUI的actionPerformed方法调用时,第二个GUI显得空白。 This does not, however, happen when JOptionPane is used. 但是,使用JOptionPane时不会发生这种情况。

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? JOptionPane做什么使它可以在actionPerformed方法中工作,为什么我的示例代码在actionPerformed方法中不能工作?

The code for the first GUI, which calls the second GUI, is as follows: 第一个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();
}
} 

The code for the second GUI is as follows: 第二个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());
}
}

This code is completely runnable with the two classes I have posted and appropriate import statements. 此代码可通过我发布的两个类和适当的import语句完全运行。 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. 第二个GUI也可以独立运行,以显示从第一个GUI调用时应显示的内容,第一个GUI包含一个正常工作的示例JOptionPane

Please help me figure out why the actionPerformed method stops only some GUIs from rendering, while others work normally! 请帮我弄清楚为什么actionPerformed方法仅阻止某些GUI呈现,而另一些GUI正常工作!

You're blocking the EDT! 您正在阻止EDT! actionPerformed is executed on the EDT, so getSelectedIndex is also and wait.await() blocks it. actionPerformed在EDT上执行,因此getSelectedIndex也是如此,并且wait.await()阻止它。 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. 即使显示第二帧,它也不会响应用户交互,因为第一个actionPerformed没有返回。

I don't understand why you need the CountDownLatch . 我不明白为什么您需要CountDownLatch getSelectedIndex can only execute once confrimBtn is pressed, so just return the selected buttons at that point. getSelectedIndex仅在按下confrimBtn才能执行,因此只需在该点返回选定的按钮即可。 This isn't the only solution - your design will eventually dictate the interaction between the classes. 这不是唯一的解决方案-您的设计最终将决定类之间的交互。

In SelectionDialog 's actionPerformed write: SelectionDialogactionPerformed写:

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

and remove the getSelectedIndex method. 并删除getSelectedIndex方法。

In OpeningGUI , add the following method OpeningGUI ,添加以下方法

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

    System.out.println(list);
}

and remove from its actionPerformed the call to getSelectedIndex . 并从其actionPerformed删除对getSelectedIndex的调用。

Notes: 笔记:

  • Instead of the screen size calculation for setLocation , you can use setLocationRelativeTo(null) . 可以使用setLocationRelativeTo(null)来代替setLocation的屏幕尺寸计算。
  • Calling setSize when you call pack right after it makes the first call redundant. setSize其后调用pack时调用setSize会使第一个调用变得多余。
  • No need to specify the generic type on the right-hand-side: 无需在右侧指定通用类型:

     selectionIndecies = new ArrayList<>(); 
  • Swing should be started on the EDT (see here ). 摆动应在EDT上开始(请参见此处 )。

  • You would probably do better with a dialog instead of another JFrame . 使用对话框而不是另一个JFrame可能会做得更好。
  • Use different ActionListener s for buttons that function differently instead of checking the source with each call. 对功能不同的按钮使用不同的ActionListener ,而不是每次调用都检查源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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