所有窗口系統均遵循統一焦點模型:桌面 GUI 與應用程序 GUI 由組件構成,任意時刻僅有一個組件擁有鍵盤焦點,後續鍵盤事件將派發給該組件。Java 作為跨平台語言,其焦點實現必須遵循此設計目標,並協調重量級組件(依賴本地 Peer)與輕量級組件(純 Java 繪製)的差異。
以 JFrame 為例,調用 setVisible(true) 觸發 peer.show(),經本地 GUI 庫繪製並註冊。用戶點擊時,OS 根據繪製信息派發事件至 JVM 進程,AWT-Windows 線程循環獲取事件,通過 Peer 標記確定目標為 JFrame,構造 AWTEvent 分派給 EDT(事件分發線程)處理。此過程中,本地系統焦點變更先於 Java 層面感知。
作者指出 Java AWT 實現中存在概念模糊:本地系統發出 Focus_Gained 時,其全局焦點變量已更新;但 Java 層面在 AWT 線程封裝 FocusEvent 入隊時,JVM 內部焦點變量尚未更新。作者建議此時不應稱為 FocusEvent,而應為 PrepareFocusEvent,以區分「系統層面已完成」與「Java 層面待確認」的狀態差。
若 setFocus 僅更新 Java 全局變量,本地系統仍認可原重量級容器焦點,導致鍵盤事件派發給錯誤進程/組件。雖在 EDT 的 mousePressed 中調用可勉強工作,但跨線程調用則不可靠。
若 setFocus 等待本地重量級容器聚焦完成再更新 Java 變量,期間 OS 可能響應其他進程(如 C++ 應用)的焦點請求,導致 Java 線程更新變量時本地焦點已易主,同樣失效。除非同步 OS 進程與 JVM 線程,但這違背「Java 服從 OS」原則。
放棄同步 setFocus,改為 requestFocus 僅登記請求。EDT 收到對應焦點事件通知後,才真正完成切換。為保證用戶體驗(點擊後即輸入),引入時間戳機制:請求滿足條件時記錄時間戳,該時段內的鍵盤事件暫存並目標指向該組件,待焦點真正獲得後批量派發。
Java 提供標準焦點遍歷模型(FocusTraversalPolicy),僅需在鍵盤事件監聽中按規則處理,無核心難點。參考官方規範:Focus Specification。
Java 焦點機制是跨平台 GUI 適配 OS 原生行為的典型難題。通過異步請求模型、時間戳預派發、EDT 串行化處理,JRE 在不違背 OS 權威的前提下,實現了符合用戶直覺的焦點行為。深入理解此機制,有助於排查焦點丟失、鍵盤事件錯亂等疑難雜症。