[英]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的一部分,我將通過顯示一個表單來處理異常。 該特定事件將被消耗,但在重新認證之后,我可以將相同的事件重新發送到應用程序,或者可能將其推送到事件隊列中。 這兩種方法都失敗了,因為我認為事件有標志,表明它是否被發布和消費。
抱歉糟糕的格式化,我還沒有想出標記。 任何幫助在這里將不勝感激。
固定:謝謝你的所有答案。 修復(我自己找不到)是在調用時捕獲會話超時異常。 應用程序彈出一個對話框,要求用戶重新進行身份驗證。 身份驗證成功后,對話框已關閉。 這令我驚訝。
我不確定,但似乎事件在顯示對話框時仍然卡在隊列中,一旦對話框關閉,它就會被傳遞給控件,無論如何。
我不會嘗試從事件的角度來解決這個問題。 事件系統無意以這種方式工作。
我將定義一個封裝與服務器交互的接口(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.