Swing/WindowClosing のバックアップ(No.21)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/WindowClosing へ行く。
- 1 (2007-10-08 (月) 16:11:56)
- 2 (2007-10-08 (月) 23:07:09)
- 3 (2008-04-22 (火) 19:05:14)
- 4 (2008-04-22 (火) 21:30:57)
- 5 (2008-04-23 (水) 01:11:16)
- 6 (2008-04-23 (水) 13:21:09)
- 7 (2008-04-23 (水) 15:28:42)
- 8 (2008-04-23 (水) 21:28:02)
- 9 (2008-04-24 (木) 12:31:06)
- 10 (2008-04-26 (土) 00:16:44)
- 11 (2008-05-07 (水) 18:35:31)
- 12 (2008-05-08 (木) 18:57:20)
- 13 (2008-05-09 (金) 12:34:44)
- 14 (2008-05-19 (月) 18:34:50)
- 15 (2008-11-25 (火) 11:25:18)
- 16 (2011-03-04 (金) 14:49:54)
- 17 (2011-03-17 (木) 22:27:27)
- 18 (2012-05-08 (火) 11:24:44)
- 19 (2013-04-11 (木) 21:58:54)
- 20 (2013-07-26 (金) 01:01:21)
- 21 (2014-09-30 (火) 01:16:09)
- 22 (2015-11-05 (木) 03:21:40)
- 23 (2015-12-21 (月) 00:38:59)
- 24 (2016-06-01 (水) 20:48:49)
- 25 (2016-08-12 (金) 15:41:24)
- 26 (2017-09-29 (金) 18:00:48)
- 27 (2018-02-24 (土) 19:46:52)
- 28 (2020-03-03 (火) 18:01:19)
- 29 (2021-08-17 (火) 20:08:30)
- 30 (2024-02-15 (木) 20:17:21)
- title: JFrameの終了をキャンセル tags: [JFrame, WindowListener] author: aterai pubdate: 2004-08-09
概要
JFrame
を閉じる前に、本当に終了してよいか、終了をキャンセルするかなどを確認するダイアログを表示します。
Screenshot
Advertisement
サンプルコード
public static final String ASTERISK_TITLEBAR = "unsaved";
private final JTextArea textarea = new JTextArea();
private final JButton saveButton = new JButton("save");
private final JFrame frame;
private final String title;
public MainPanel(final JFrame frame) {
super(new BorderLayout());
this.frame = frame;
this.title = frame.getTitle();
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addPropertyChangeListener(new PropertyChangeListener() {
@Override public void propertyChange(PropertyChangeEvent e) {
if(ASTERISK_TITLEBAR.equals(e.getPropertyName())) {
Boolean unsaved = (Boolean)e.getNewValue();
frame.setTitle((unsaved?"* ":"")+title);
}
}
});
frame.addWindowListener(new 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
}
});
textarea.setText("Test Test Test");
textarea.getDocument().addDocumentListener(new DocumentListener() {
@Override public void insertUpdate(DocumentEvent e) {
fireUnsavedFlagChangeEvent(true);
}
@Override public void removeUpdate(DocumentEvent e) {
fireUnsavedFlagChangeEvent(true);
}
@Override public void changedUpdate(DocumentEvent e) {}
});
saveButton.setEnabled(false);
saveButton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent ae) {
System.out.println("Save(dummy)");
fireUnsavedFlagChangeEvent(false);
}
});
add(new JScrollPane(textarea));
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
box.add(new JButton(new AbstractAction("exit") {
@Override public void actionPerformed(ActionEvent e) {
System.out.println("exit button");
maybeExit();
}
}));
box.add(Box.createHorizontalStrut(5));
box.add(saveButton);
add(box, BorderLayout.SOUTH);
setPreferredSize(new Dimension(320, 240));
}
private void maybeExit() {
if(title.equals(frame.getTitle())) {
System.out.println("The document has already been saved,"+
" exit without doing anything.");
frame.dispose();
return;
}
java.awt.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.WARNING_MESSAGE, null, options, options[0]);
if(retValue==JOptionPane.YES_OPTION) {
System.out.println("exit");
//boolean ret = dummyDocumentSaveMethod();
//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");
}
}
private void fireUnsavedFlagChangeEvent(boolean unsaved) {
if(unsaved) {
saveButton.setEnabled(true);
firePropertyChange(ASTERISK_TITLEBAR, Boolean.FALSE, Boolean.TRUE);
}else{
saveButton.setEnabled(false);
firePropertyChange(ASTERISK_TITLEBAR, Boolean.TRUE, Boolean.FALSE);
}
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、アプリケーションの終了時に、ドキュメントが保存されているかどうかで処理を変更するために、ウィンドウイベントを受け取るためのリスナーを設定しています。
WindowAdapter#windowClosing(WindowEvent e)
- システムメニューでウィンドウを閉じようとしたときに呼び出されるリスナーのメソッド
OS
がWindows
なら、Alt+F4キーを押す- タイトルバー左上にあるアイコンをクリックし、ポップアップメニューで閉じるを選択
- タイトルバー右上の×ボタンをクリック
JButton
やJMenu
などをクリックした時に、対象となるframe
のwindowClosing
を呼び出したい場合は、frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
frame.dispose();
では、このメソッドは呼び出されない
- システムメニューでウィンドウを閉じようとしたときに呼び出されるリスナーのメソッド
WindowAdapter#windowClosed(WindowEvent e)
frame.dispose()
で、ウィンドウがクローズされたときに呼び出されるリスナーのメソッドwindowClosing
の後、自動的にwindowClosed
が呼び出されるのは、WindowConstants.DISPOSE_ON_CLOSE
の場合のみ- このサンプルでは、
Web Start
から起動しても終了できるように、frame.dispose()
すれば必ず呼び出されるこのメソッド中でSystem.exit(0);
を使い、JVM
ごとシャットダウンしているWeb Start
でシャットダウンする必要性については、When DISPOSE_ON_CLOSE met WebStartを参考に
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
のタイトルを変更
参考リンク
コメント
- 私は以前 この終了をキャンセルするかどうかなどを確認するダイアログを作成したことがあります。あなたのソースコードは 参考のかいがあると思います。でも ひとつの問題があるんですけど、
textarea
に入力した文字列を削除する場合はJFrame
のタイトルが変化されていません、どうですか? -- そうがい?- こんばんは。「
123
->12345
(45
追加)->123
(45
削除)」と追加、削除をして元の状態に戻っても、タイトルが変化しないのは、仕様です。比較のコストが大きくなってしまいそうで嫌なので避けています。 -- aterai
- こんばんは。「
- 変更をアスタリスクに変更、コードの構成を変更、スクリーンショット更新 -- aterai
- メモ: シャットダウンフック API の設計 -- aterai