2015-05-07 2 views
1

Я пытаюсь сделать набор из 2 графических интерфейсов: один, когда нажата кнопка, вызывает другую, которая, на основе которой нажата кнопка во втором графическом интерфейсе, возвращает значение к первому графическому интерфейсу. К сожалению, при вызове из первого метода GUI actionPerformed второй графический интерфейс выглядит пустым. Это не происходит, однако, когда используется JOptionPane.JFrame не рисует контент при вызове внутри ActionListener

Что делает JOptionPane, что позволяет ему работать внутри метода actionPerformed, и почему мой пример кода не работает внутри метода actionPerformed?

Код для первого графического интерфейса пользователя, который вызывает второй графический интерфейс, выглядит следующим образом:

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

Код для второго графического интерфейса выглядит следующим образом:

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 содержит пример JOptionPane, который работает нормально.

Пожалуйста, помогите мне выяснить, почему метод actionPerformed останавливает только некоторые графические интерфейсы от рендеринга, в то время как другие работают нормально!

+0

Помог ли мой ответ решить вашу проблему? – user1803551

+0

Да, да. Поскольку я вызывал SelectionDialog из нескольких мест в моем коде, чтобы делать разные вещи с возвращаемым значением, я не видел хорошего способа реализовать ваше предложение о вызове метода из первого графического интерфейса. Однако, чтобы решить мою проблему блокировки EDT, я смог изменить Modality, которая решила мою проблему! Спасибо за предложение относительно EDT. Это был первый случай, когда любая такая проблема с резьбой была доведена до моего сведения. –

+0

Кроме того, общие комментарии к моему коду были очень впечатляющими! –

ответ

1

Вы блокируете EDT! actionPerformed выполнен на EDT, поэтому getSelectedIndex также и wait.await() блокирует его. Обратите внимание, что как только это произойдет, первый кадр также не отвечает (и минимизация и сведение к минимуму кадров даже не будут их рисовать). Даже если бы второй кадр был показан, он не ответил бы на взаимодействие пользователя, потому что первый actionPerformed не вернулся.

Я не понимаю, зачем вам нужен CountDownLatch. getSelectedIndex может выполняться только один раз, когда нажата confrimBtn, поэтому просто верните выбранные кнопки в этой точке. Это не единственное решение - ваш дизайн в конечном итоге будет определять взаимодействие между классами.

В 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.

Примечания:

  • Вместо расчета размера экрана для setLocation, вы можете использовать setLocationRelativeTo(null).
  • Вызов setSize, когда вы позвоните pack сразу после того, как он делает первый звонок избыточным.
  • Нет необходимости указывать общий тип на правой руке стороне:

    selectionIndecies = new ArrayList<>(); 
    
  • свинг должен быть запущен на EDT (см here).

  • Возможно, вам будет лучше с диалогом вместо другого JFrame.
  • Используйте разные кнопки ActionListener s для кнопок, которые работают по-разному, вместо проверки источника при каждом вызове.
Смежные вопросы