プログラマメモ2 - programmer no memo2

SWING モーダルダイアログを出した後に、フレームを表示すると操作ができなくなってしまう。 2006/06/22
2006/11/26

「モーダルダイアログを出した後に、フレームを表示すると操作ができなくなってしまう。」

という現象を発現させてみます。



発現手順

  1. フレームを表示します。
  2. モーダルダイアログを表示します。
  3. 別スレッドでフレームを重ねて表示します。




モーダルダイアログをフレームでサンドウィッチする状態をつくると、中にはさまれたダイアログにたいする操作ができなくなります。



ようするにプログラムの仕組みが悪いといわれればそれまですが。。。



問題を発現するためのサンプルコードです。

実行環境によっては発現しないかもしれません。ダイアログを別フレームで覆い隠してあとから表示すれば発現すると思います。





import java.awt.Rectangle;



import javax.swing.JFrame;

import javax.swing.JOptionPane;



public class TestHideDailog {



public static boolean showYesNoDialog(String title, String message) {



int ret = JOptionPane.showConfirmDialog(null, message, title,

JOptionPane.YES_NO_OPTION);

if (ret == JOptionPane.YES_OPTION)

return true;

return false;

}



public static Runnable createDialogRunnable(final String title, final String message){

return new Runnable() {

public void run() {

showYesNoDialog(title, message);

}};

}

public static void main(String[] args) throws InterruptedException {





Runnable runnableFrame = new Runnable() {

public void run() {

JFrame frame = new JFrame();

frame.setBounds(new Rectangle(300, 300, 900, 500));

frame.setVisible(true);

}

};



new Thread(runnableFrame).start();

new Thread(createDialogRunnable("dialog1", "@_@!1")).start();

new Thread(createDialogRunnable("dialog2", "@_@!2")).start();

new Thread(createDialogRunnable("dialog3", "@_@!3")).start();

Thread.sleep(1000);//need wait!!

new Thread(runnableFrame).start();

}

}





下記に解決のためのサンプルコードを提示します。



解決策として、モーダルダイアログからフォーカスがはずれた場合にダイアログにフォーカスを戻すようにしてみます。



下記のコードは一部、JDKのJOptionPaneのコードを参考にしています。



import java.awt.Component;

import java.awt.HeadlessException;

import java.awt.Rectangle;

import java.awt.event.WindowEvent;

import java.awt.event.WindowFocusListener;



import javax.swing.Icon;

import javax.swing.JDialog;

import javax.swing.JFrame;

import javax.swing.JOptionPane;

import javax.swing.SwingUtilities;



public class TestHideDailog_solved {



public static boolean showYesNoDialog(String title, String message) {



int ret = showOptionDialog(null, message, title,

JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,

null, null);

if (ret == JOptionPane.YES_OPTION)

return true;

return false;

}



public static Runnable createDialogRunnable(final String title,

final String message) {

return new Runnable() {

public void run() {

showYesNoDialog(title, message);

}

};

}



public static void main(String[] args) throws InterruptedException {



Runnable runnableFrame = new Runnable() {

public void run() {

JFrame frame = new JFrame();

frame.setBounds(new Rectangle(300, 300, 900, 500));

frame.setVisible(true);

}

};



new Thread(runnableFrame).start();

new Thread(createDialogRunnable("dialog1", "@_@!1")).start();

new Thread(createDialogRunnable("dialog2", "@_@!2")).start();

new Thread(createDialogRunnable("dialog3", "@_@!3")).start();

Thread.sleep(1000);// need wait!!

new Thread(runnableFrame).start();

}



/**

* swing(JOptionPane) java source is be used as reference.

*/

public static int showOptionDialog(Component parentComponent,

Object message, String title, int optionType, int messageType,

Icon icon, Object[] options, Object initialValue)

throws HeadlessException {



JOptionPane pane = new JOptionPane(message, messageType, optionType,

icon, options, initialValue);



pane.setInitialValue(initialValue);

pane.setComponentOrientation(((parentComponent == null) ? JOptionPane

.getRootFrame() : parentComponent).getComponentOrientation());



final JDialog dialog = pane.createDialog(parentComponent, title);





WindowFocusListener focusListener = new WindowFocusListener() {



public void windowGainedFocus(WindowEvent e) {

}



/*

* フォーカスをダイアログに変更する。

*/

public void windowLostFocus(WindowEvent e) {

SwingUtilities.invokeLater(new Runnable() {

public void run() {

dialog.requestFocus();

}

});

}



};

dialog.addWindowFocusListener(focusListener);



pane.selectInitialValue();

dialog.show();

dialog.dispose();



Object selectedValue = pane.getValue();



if (selectedValue == null)

return JOptionPane.CLOSED_OPTION;

if (options == null) {

if (selectedValue instanceof Integer)

return ((Integer) selectedValue).intValue();

return JOptionPane.CLOSED_OPTION;

}

for (int counter = 0, maxCounter = options.length; counter < maxCounter; counter++) {

if (options[counter].equals(selectedValue))

return counter;

}

return JOptionPane.CLOSED_OPTION;

}

}









別の解決策です。 JDK1.5(Tiger)を使用している場合、 Window#setAlwaysOnTop(true)を使用すると解決します。 参考: http://www.javainthebox.net/laboratory/J2SE1.5/GUI/WindowLocation/WindowLocation.html





JDK1.5にはWindow操作のためのいくつかの機能が追加されているようです。



import java.awt.Component;

import java.awt.HeadlessException;

import java.awt.Rectangle;



import javax.swing.Icon;

import javax.swing.JDialog;

import javax.swing.JFrame;

import javax.swing.JOptionPane;



public class TestHideDailog_solved_jdk15 {



public static boolean showYesNoDialog(String title, String message) {



int ret = showOptionDialog(null, message, title,

JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,

null, null);

if (ret == JOptionPane.YES_OPTION)

return true;

return false;

}



public static Runnable createDialogRunnable(final String title,

final String message) {

return new Runnable() {

public void run() {

showYesNoDialog(title, message);

}

};

}



public static void main(String[] args) throws InterruptedException {



Runnable runnableFrame = new Runnable() {

public void run() {

JFrame frame = new JFrame();

frame.setBounds(new Rectangle(300, 300, 900, 500));

frame.setVisible(true);

}

};



new Thread(runnableFrame).start();

new Thread(createDialogRunnable("dialog1", "@_@!1")).start();

new Thread(createDialogRunnable("dialog2", "@_@!2")).start();

new Thread(createDialogRunnable("dialog3", "@_@!3")).start();

Thread.sleep(1000);// need wait!!

new Thread(runnableFrame).start();

}



/**

* swing(JOptionPane) java source is be used as reference.

*/

public static int showOptionDialog(Component parentComponent,

Object message, String title, int optionType, int messageType,

Icon icon, Object[] options, Object initialValue)

throws HeadlessException {



JOptionPane pane = new JOptionPane(message, messageType, optionType,

icon, options, initialValue);



pane.setInitialValue(initialValue);

pane.setComponentOrientation(((parentComponent == null) ? JOptionPane

.getRootFrame() : parentComponent).getComponentOrientation());



final JDialog dialog = pane.createDialog(parentComponent, title);



pane.selectInitialValue();

dialog.setAlwaysOnTop(true);

dialog.show();

dialog.dispose();



Object selectedValue = pane.getValue();



if (selectedValue == null)

return JOptionPane.CLOSED_OPTION;

if (options == null) {

if (selectedValue instanceof Integer)

return ((Integer) selectedValue).intValue();

return JOptionPane.CLOSED_OPTION;

}

for (int counter = 0, maxCounter = options.length; counter < maxCounter; counter++) {

if (options[counter].equals(selectedValue))

return counter;

}

return JOptionPane.CLOSED_OPTION;

}

}



: