簡體   English   中英

如何在java中重新發送任何AWTEvent?

[英]How do I redispatch ANY AWTEvent in java?

我有一個在基於Java 1.5 Swing的應用程序中實現的功能。 如果在處理AWTEvent時發生特殊異常,我必須彈出一個替代表單,解決問題並繼續處理原始事件。 當我將事件重新發送到組件時,沒有任何反應。 當我將事件推入事件隊列時,沒有任何事情發生。 我假設事件中有一些狀態字段將其標記為已處理,因此組件不會將其拾取。 到目前為止,我找不到重新創建事件克隆的方法。 自定義事件在這里沒有幫助,因為我希望先前的事件得到處理。

在swing應用程序中,現有的事件隊列被內部隊列替換。

private class ApplicationEventQueue extends EventQueue  
{  
    private final ArrayList listeners=new ArrayList();  
    protected void initialize()  
    {  
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(this);  
    }
.
.
.
}

作為dispatch事件的一部分,該類攔截調用ans委托給超類。 如果發生異常,它將彈出一個消息框,其中包含“抱歉為此不便”的消息。

@Override  
protected void dispatchEvent(AWTEvent event)  
{  
    try  
    {  
        super.dispatchEvent(event);  
        if (peekEvent() != null && userEventDispatched)  
        {  
            raiseIdleEvent();  
            userEventDispatched = false;  
        }  
        else  
        {  
            int eventId = event.getID();  
            if (eventId == KeyEvent.KEY_TYPED || eventId == MouseEvent.MOUSE_CLICKED)  
            {  
                userEventDispatched = true;  
            }  
        }  
    }  
    catch (Throwable ex)  
    {  
        onError(ex);  
    }  
}

所需的功能是能夠超時用戶會話。 會話超時時,服務器將拋出特定異常。 超時時,將提示用戶重新登錄,並且將中止原始操作。 我想要做的是,作為onError的一部分,我將通過顯示一個表單來處理異常。 該特定事件將被消耗,但在重新認證之后,我可以將相同的事件重新發送到應用程序,或者可能將其推送到事件隊列中。 這兩種方法都失敗了,因為我認為事件有標志,表明它是否被發布和消費。

  1. 事件發生可能是任何事件(無論是擊鍵鼠標)。
  2. 定義自定義事件無法解決問題,因為我需要重播同一事件。
  3. 我考慮過克隆事件,但AWTEvent不支持克隆。
  4. 通過序列化然后反序列化事件的深層復制不起作用,因為一些被調度的事件不可序列化。
  5. 我正在考慮通過反射重置事件中的任何狀態變量,但這似乎很危險。

抱歉糟糕的格式化,我還沒有想出標記。 任何幫助在這里將不勝感激。

固定:謝謝你的所有答案。 修復(我自己找不到)是在調用時捕獲會話超時異常。 應用程序彈出一個對話框,要求用戶重新進行身份驗證。 身份驗證成功后,對話框已關閉。 這令我驚訝。

我不確定,但似乎事件在顯示對話框時仍然卡在隊列中,一旦對話框關閉,它就會被傳遞給控件,​​無論如何。

我不會嘗試從事件的角度來解決這個問題。 事件系統無意以這種方式工作。

我將定義一個封裝與服務器交互的接口(X)。 實現Y將保存最后一個服務器請求的參數。 發生超時后,用戶重新進行了身份驗證,然后我可以要求Y重新發送最后一個請求。

額外的好處:由於X是一個接口,這簡化了測試,因為可以用模擬對象替換Y來測試GUI,測試代碼可以在沒有GUI存在的情況下調用Y.

更新:

這是一種使用SwingWorker在bg線程上運行服務器交互的方法。 ChangeEvent用於將結果返回給EDT進行處理。 SwingWorker的發布/過程用於處理用戶交互以進行重新驗證。 SwingWorker的一個好處是,如果服務器需要很長時間來響應,UI仍然可以響應重繪事件。

class Test extends JPanel {
    private JButton b;
    public Test() {
        b = new JButton(new AbstractAction("Do something") {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JButton btn = (JButton) e.getSource();
                btn.setEnabled(false);
                Object req = new Object(); // Replace w/ apropriate type
                new RequestDispatch(req, new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        final Object req = e.getSource();
                        // Do something with data from 'req'
                        btn.setEnabled(true);
                    }
                });
            }
        });
        add(b);
    }
}

class RequestDispatch extends SwingWorker<Object, Void> {
    private enum DispatchState { Ready, Running, Canceled }
    private final ChangeListener listener;
    private DispatchState dstate = DispatchState.Ready;
    private final Object req;
    RequestDispatch(Object req, ChangeListener listener)
    {
        this.req = req;
        this.listener = listener;
        execute();
    }
    @Override
    protected Object doInBackground() throws Exception {
        while (true) {
            DispatchState st = getDState();
            if (st == DispatchState.Ready)
            {
                try {
                    setDstate(DispatchState.Running);
                    // send request to the server, update req with response
                    return req;
                }
                catch (TimeoutException ex) {
                    this.publish((Void)null);
                    synchronized (this) {
                        wait();
                    }
                }
            }
            if (st == DispatchState.Canceled) {
                return req;
            }
        }
    }
    @Override
    protected void process(List<Void> chunks) {
        // Session has timed out
        // Ask the user to re-authenticate
        int result = JOptionPane.showConfirmDialog(null, "Login");
        if (result == JOptionPane.CANCEL_OPTION) {
            setDstate(DispatchState.Canceled);
        }
        else {
            setDstate(DispatchState.Ready);
        }
    }
    private synchronized DispatchState getDState() {
        return dstate;
    }
    private synchronized void setDstate(DispatchState dstate) {
        this.dstate = dstate;
        notifyAll();
    }
}

您可以將原始事件放入一個自定義包裝類,其名稱類似於AWTEventWrapper ,它擴展了AWTEvent 然后,該類覆蓋所有方法並將它們委托給包裝事件,除了consume()isConsumed() (保留默認實現),以便您可以假設事件未被使用,並允許它被處理再一次。

看看JDK6代碼, AWTEvent.consumed不能設置為false,因為只有consume() ,沒有unconsume()

我認為OP自己的#5是唯一的方式,雖然確實很危險。 (我實際上AWTEvent#get_InputEvent_CanAccessSystemClipboard()了如何從AWTEvent#get_InputEvent_CanAccessSystemClipboard()設置私有字段。)

在你的onError() ,你可以創建另一個覆蓋getNextEvent和boolean標志的事件隊列,這樣當你調用EventQueue#push() ,新隊列會將所有當前事件保存在列表中,然后執行錯誤通知,最后彈出新隊列,在拋出異常的情況下consumed翻轉,重新發送它,然后是您在列表中保存的早期事件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM