モデルとビューを備えたSwingアプリケーションがあります。ビュー(GUI)には多くのコンポーネントがあり、それぞれがモデルオブジェクトのプロパティにマッピングされ、その値が表示されます。
UIで値が変更されたときに、一部のモデルプロパティの更新を自動的にトリガーするUIコンポーネントがいくつかあります。これには、UIでモデル全体をリロードする必要があります。このようにして、UIでモデルをリロードするたびに別のモデルのリロードがトリガーされるため、無限の更新ループに入ります。
モデルからUIフィールドを設定しているときに、リスナー通知を一時的に抑制するために使用したいロードプロセスを示すフラグがあります。だから私の質問は:
Swingで一部のコンポーネントのリスナーを削除して再接続せずに一時的に無効にする方法はありますか?
リスナーに共通の基本クラスを使用でき、その中に、リスナーをオンまたはオフにする静的メソッドがあります。
_public abstract class BaseMouseListener implements ActionListener{
private static boolean active = true;
public static void setActive(boolean active){
BaseMouseListener.active = active;
}
protected abstract void doPerformAction(ActionEvent e);
@Override
public final void actionPerformed(ActionEvent e){
if(active){
doPerformAction(e);
}
}
}
_
リスナーは、doPerformAction()
ではなくactionPerformed()
を実装する必要があります。
(これはエンタープライズシナリオではひどいですが、Swingのような単一VMモデルでは、問題なく機能するはずです)
Stackoverflowを検索しているときに、この質問を見つけました。私は自分の意見/答えを追加しようと思いました。
Swingでイベントリスナーを一時的に無効にするのは本当に悪い考えです。コードが壊れている(または他の問題が発生している)場合、アプリケーションを元に戻すことができない可能性があります。ユーザーやその他のイベントに応答します。
ユーザーイベントを破棄する(応答するが何もしない)場合は、イベントを無視できるガラスペインを使用できます。
EDTがビジー状態で(これも可能な限り回避する必要があります)、その期間のユーザーアクションを破棄したい場合でも、ガラスペインを使用し、すべてのイベントが応答された後、invokeLaterを使用してペインを削除できます(ガラス板によって無視されます)に。
SSCEを含む完全な詳細は、この質問にあります。
通常、APIの変更またはユーザーの変更を示すフラグを使用します。リスナーごとにフラグを確認し、APIが変更されている場合は戻るだけです。
上記のように、GlassPaneはこの点で役立ちます。簡単な例を次に示します。
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.SwingWorker;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import Java.awt.Color;
import Java.awt.Cursor;
import Java.awt.Graphics;
import Java.awt.FlowLayout;
import Java.awt.event.ActionEvent;
import Java.awt.event.ActionListener;
import Java.awt.event.MouseAdapter;
import Java.awt.event.MouseEvent;
public class GlassPaneExample extends JFrame implements ActionListener {
private JButton btnDisable;
private JButton btnTestOne;
private JButton btnTestTwo;
private MyGlassPane glass;
private boolean actionAllowed = true;
public GlassPaneExample() {
// init JFrame graphics
setBounds(300, 300, 300, 110);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setVisible(true);
// init buttons
btnTestOne = new JButton("Button one");
add(btnTestOne);
btnTestTwo = new JButton("Button two");
add(btnTestTwo);
btnDisable = new JButton("Disable ActionListeners for 2 seconds");
add(btnDisable);
// create Glass pane
glass = new MyGlassPane();
setGlassPane(glass);
// add listeners
btnTestOne.addActionListener(this);
btnTestTwo.addActionListener(this);
btnDisable.addActionListener(this);
}
public static void main(String[] args) {
new GlassPaneExample();
}
@Override
public void actionPerformed(ActionEvent e) {
JButton src = (JButton)e.getSource();
if (src.equals(btnDisable)) {
// setting glasspane visibility to 'true' allows it to receive mouse events
glass.setVisible(true);
setCursor(new Cursor(Cursor.WAIT_CURSOR));
SwingWorker sw = new SwingWorker() {
@Override
protected Object doInBackground()
throws Exception {
Thread.sleep(2000);
return null;
}
@Override
public void done() {
// set cursor and GlassPane back to default state
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
glass.setVisible(false);
// allow actions to be received again
actionAllowed = true;
}
};
sw.execute();
} else if (actionAllowed) {
if (src.equals(btnTestOne)) {
JOptionPane.showMessageDialog(this, "BUTTON ONE PRESSED");
} else if (src.equals(btnTestTwo)) {
JOptionPane.showMessageDialog(this, "BUTTON TWO PRESSED");
}
}
}
class MyGlassPane extends JPanel {
public MyGlassPane() {
setOpaque(false);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
actionAllowed = false;
}
});
}
//Draw an cross to indicate glasspane visibility
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.drawLine(0, 0, getWidth(), getHeight());
g.drawLine(getWidth(), 0, 0, getHeight());
}
}
}
あなたのために働くかもしれない1つのオプションは、その間のイベントをブロックするためにロード中にガラス板を上げることです: http://download.Oracle.com/javase/tutorial/uiswing/components/rootpane。 html#glasspane
この質問 同様の問題のように見えますが、満足のいく解決策はありません。
私はこれが 記事 自分のデザインを批判的に調べるのに役立つと思いました。
Swingで一部のコンポーネントのリスナーを削除して再接続せずに、それらをグローバルに一時的に無効にする方法はありますか?
すべてのJComponent
は EventListenerList
を維持します。これは、サブクラスからアクセスできます。必要に応じて、いつでもリストを直接操作するか、目的の動作をカスタム実装に組み込むことができます EventListener