在 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.
我不明白你为什么需要 CountDownLatch
。 getSelectedIndex
只能在按下 confrimBtn
后执行,因此此时只需 return 选定的按钮。这不是唯一的解决方案 - 您的设计最终将决定 类.
之间的交互
在SelectionDialog
的actionPerformed
中写:
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
,而不是在每次调用时检查来源。
我正在尝试制作一组 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.
我不明白你为什么需要 CountDownLatch
。 getSelectedIndex
只能在按下 confrimBtn
后执行,因此此时只需 return 选定的按钮。这不是唯一的解决方案 - 您的设计最终将决定 类.
在SelectionDialog
的actionPerformed
中写:
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)
. 而不是 - 在调用
pack
时调用setSize
会使第一个调用变得多余。 无需在右侧指定泛型:
selectionIndecies = new ArrayList<>();
Swing 应该在 EDT 开始(参见 here)。
- 您可能会使用对话框而不是另一个对话框做得更好
JFrame
。 - 对功能不同的按钮使用不同的
ActionListener
,而不是在每次调用时检查来源。
setLocation
的屏幕尺寸计算