JFrameの終了をキャンセル
Total: 51614
, Today: 3
, Yesterday: 7
Posted by aterai at
Last-modified:
概要
JFrame
を閉じる前に、本当に終了してよいか、終了をキャンセルするかなどを確認するダイアログを表示します。
Screenshot
Advertisement
サンプルコード
class SaveHandler extends WindowAdapter implements DocumentListener, ActionListener {
// public static final String ASTERISK_TITLEBAR = "unsaved";
public static final String CMD_SAVE = "save";
public static final String CMD_EXIT = "exit";
private final JFrame frame;
private final String title;
private final List<JComponent> list = new ArrayList<>();
public SaveHandler(JFrame frame) {
super();
this.frame = frame;
this.title = frame.getTitle();
}
// WindowAdapter
@Override public void windowClosing(WindowEvent e) {
System.out.println("windowClosing");
maybeExit();
}
@Override public void windowClosed(WindowEvent e) {
System.out.println("windowClosed");
System.exit(0); // webstart
}
// ActionListener
@Override public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (CMD_EXIT.equals(cmd)) {
maybeExit();
} else if (CMD_SAVE.equals(cmd)) {
fireUnsavedFlagChangeEvent(false);
}
}
// DocumentListener
@Override public void insertUpdate(DocumentEvent e) {
fireUnsavedFlagChangeEvent(true);
}
@Override public void removeUpdate(DocumentEvent e) {
fireUnsavedFlagChangeEvent(true);
}
@Override public void changedUpdate(DocumentEvent e) {
/* not needed */
}
private void maybeExit() {
if (title.equals(frame.getTitle())) {
System.out.println(
"The document has already been saved, exit without doing anything.");
frame.dispose();
return;
}
Toolkit.getDefaultToolkit().beep();
Object[] options = {"Save", "Discard", "Cancel"};
int retValue = JOptionPane.showOptionDialog(
frame, "<html>Save: Exit & Save Changes<br>"
+ "Discard: Exit & Discard Changes<br>Cancel: Continue</html>",
"Exit Options", JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.INFORMATION_MESSAGE, null, options, options[0]);
if (retValue == JOptionPane.YES_OPTION) {
System.out.println("exit");
// boolean ret = sampleDocumentSaveMethod();
// if (ret) { // saved and exit
// frame.dispose();
// } else { // error and cancel exit
// return;
// }
frame.dispose();
} else if (retValue == JOptionPane.NO_OPTION) {
System.out.println("Exit without save");
frame.dispose();
} else if (retValue == JOptionPane.CANCEL_OPTION) {
System.out.println("Cancel exit");
}
}
public void addEnabledFlagComponent(JComponent c) {
list.add(c);
}
public void removeEnabledFlagComponent(JComponent c) {
list.remove(c);
}
private void fireUnsavedFlagChangeEvent(boolean unsaved) {
frame.setTitle(String.format("%s%s", unsaved ? "* " : "", title));
for (JComponent c : list) {
c.setEnabled(unsaved);
}
}
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、アプリケーションの終了時に、ドキュメントが保存されているかどうかで処理を変更するために、ウィンドウイベントを受け取るためのリスナーを設定しています。
WindowListener#windowClosing(WindowEvent e)
- システムメニューでウィンドウを閉じようとしたときに呼び出されるリスナーのメソッド
OS
がWindows
ならAlt+F4キーを押す- タイトルバー左上にあるアイコンをクリックしポップアップメニューで閉じるを選択
- タイトルバー右上の
×
ボタンをクリック JButton
やJMenu
などをクリックした時に対象となるframe
のwindowClosing
を呼び出したい場合は、frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
frame.dispose();
ではこのメソッドは呼び出されない
- システムメニューでウィンドウを閉じようとしたときに呼び出されるリスナーのメソッド
WindowListener#windowClosed(WindowEvent e)
frame.dispose()
でウィンドウがクローズされたときに呼び出されるリスナーのメソッドwindowClosing
の後自動的にwindowClosed
が呼び出されるのはWindowConstants.DISPOSE_ON_CLOSE
の場合のみ- このサンプルでは
Web Start
から起動しても終了できるようにframe.dispose()
すれば必ず呼び出されるこのメソッド中でSystem.exit(0);
を使いJVM
ごとシャットダウンしている
JFrame#setDefaultCloseOperation
メソッドでタイトルバー右上の×ボタンをクリック(デフォルトの終了処理)し、windowClosing
が呼ばれた後(このためwindowClosing
中で変更しても有効)の動作を設定可能- これらの動作については
JFrame#processWindowEvent(WindowEvent)
のソースを参照
- これらの動作については
WindowConstants.DO_NOTHING_ON_CLOSE
windowClosing
が呼ばれた後になにもしない(終了しない)return;
と同じ- このサンプルでは
WindowConstants.DO_NOTHING_ON_CLOSE
を設定しているが、システムメニューでウィンドウを閉じても、下のexit
ボタンと同じ処理になるようにwindowClosing
の中で終了処理を行うメソッド(maybeExit()
)を呼び出し、そこでドキュメントの保存状態によってframe.dispose();
を呼んでいる
WindowConstants.HIDE_ON_CLOSE
windowClosing
が呼ばれた後でウィンドウは非表示になるsetVisible(false);
と同じ- 初期値
WindowConstants.DISPOSE_ON_CLOSE
windowClosing
が呼ばれた後でウィンドウは破棄されるdispose();
と同じdispose()
されるのでこの後windowClosed
が呼び出される
WindowConstants.EXIT_ON_CLOSE
windowClosing
が呼ばれた後でJVM
がシャットダウンされるSystem.exit(0);
と同じdispose()
されないのでwindowClosed
は呼び出されない
- テキストが変更された場合、タイトル文字列の先頭にアスタリスクを付けることで保存状態の可視化と保持を行っている
- ドキュメントに文字列が追加されたとき、ソース側から
firePropertyChange
などでリスナーに変更をイベントで報告 - リスナー側ではこのイベントを受け
JFrame
のタイトルを変更