Sample : JDK1.5.0_10以上使用時のKey処理不具合を回避する
実装サンプル:なし
[サンプル提供]:
jp.ne.mki.wedge.education.focusmanager.EnqueueNoWaitKeyboardFocusManager
回避する不具合の概要↑top
JDK1.5.0_10以降 (2007/06/05現在 JDK1.5.0_11, JDK1.5.0_12 も含む) のJDKを使用した場合、
フォーカスを取得していないComponentに対して、KeyEventが処理されComponent
に反映してしまう現象が発生します。
この現象は、重い処理中(メインスレッドにて何かの処理中)に、TABにてFocus移動を含んだ
KEY入力を行った際に、テキストのフォーカス移動"後" (After処理後) に Keyが反映されるという
不具合を招くことになり、内部データと表示データとの整合性が合わない という現象になって
現れます。
JDK1.5.0_10での変更の詳細については、Javaネイティブコードに達する為、詳細把握はできていませんが、、 java.awt.DefaultKeyboardFocusManager#enqueueKeyEvents(long, java.awt.Component)の 第一引数の long の時間を、java.awt.DefaultKeyboardFocusManager#dispatchKeyEvent(java.awt.event.KeyEvent) のタイミングの java.awt.event.KeyEvent#getWhen() の値に置き換える処理を組み込むことにより、 JDK1.5.0_09以前の動きと擬似的に同じできるのを確認できたため、 その処理を組み込んだKeyboardFocusManagerをサンプルとしての提供になります。
この対応方法は、根本的な解決ではないため、これによる副作用がある可能性があります。
(製品のテストケースは全て通るのは確認していますが、予想外の副作用が潜んでいる可能性があります。)
このJDK1.5.0_09とJDK1.5.0_10との挙動の差は JDK1.5.0_10 のリリースノートに記述されている、 バグID:6448190 「JFrame と JButton でフォーカスの問題」 での修正に依るものと考えられますので、 その修正よる不具合解決はできなくなる可能性があります。
この不具合はSwingハードコーディングにてInputVerifierを使ったテストでも不具合が発生するため、
Swing自体の不具合と判断しています。
そのため、製品本体にて不具合回避を行うのではなく、サンプルとしての提供となりますので、ご了承下さい
また、 新しいJDKがリリースされて、この不具合が解決された場合、
このKeyboardFocusManagerクラスを設定せずとも 不具合が解消される可能性があります。
不具合状態での動き↑top
Swingハードコーディングの場合↑top
Buttonの処理にてwaitをかけた場合
JButton, JTextField, JTextField を配置し、Buttonにフォーカスがある状態で、SPACE, TAB, 1, TAB, 1 Keyを続けて押下
してテストを行いました。
各Componentにはそれぞれ、java.awt.event.FocusListener,
javax.swing.InputVerifier を設定しています。
- JDK1.5.0_08の場合
正常に動作します
操作orイベント KEYイベントの種類 getWhenの時間 処理Component FocusListener#focusGaind on BUTTON(0 SPACE KEY_PRESSED 1179812786328 on BUTTON(0) SPACE KEY_TYPED 1179812786328 on BUTTON(0) SPACE KEY_RELEASED 1179812786468 on BUTTON(0) ActionListener#actionPerformed on BUTTON(0) TAB KEY_PRESSED 1179812787031 on BUTTON(0) InputVerifier#verify on BUTTON(0) TAB KEY_TYPED 1179812787031 on BUTTON(0) FocusListener#focusGaind on TEXT(1) TAB KEY_RELEASED 1179812787109 on TEXT(1) 1 KEY_PRESSED 1179812787172 on TEXT(1) 1 KEY_TYPED 1179812787172 on TEXT(1) 1 KEY_RELEASED 1179812787250 on TEXT(1) TAB KEY_PRESSED 1179812787312 on TEXT(1) InputVerifier#verify on TEXT(1) TAB KEY_TYPED 1179812787312 on TEXT(1) FocusListener#focusGaind on TEXT(2) TAB KEY_RELEASED 1179812787406 on TEXT(2) 1 KEY_PRESSED 1179812787468 on TEXT(2) 1 KEY_TYPED 1179812787468 on TEXT(2) 1 KEY_RELEASED 1179812787531 on TEXT(2)
- JDK1.5.0_10の場合
処理中の2回目のTABによるフォーカス移動が行われない。
最後の 1 のKEYは、TEXT(1)のInputVefirier#verifyが処理された後に、TEXT(1)に対して反映している。
そのため、InputVefirierではチェック漏れが発生するという不具合が発生する。
操作orイベント KEYイベントの種類 getWhenの時間 処理Component FocusListener#focusGaind on BUTTON(0) SPACE KEY_PRESSED 1179812682390 on BUTTON(0) SPACE KEY_TYPED 1179812682390 on BUTTON(0) SPACE KEY_RELEASED 1179812682515 on BUTTON(0) ActionListener#actionPerformed on BUTTON(0) TAB KEY_PRESSED 1179812683172 on BUTTON(0) InputVerifier#verify on BUTTON(0) TAB KEY_TYPED 1179812683172 on BUTTON(0) FocusListener#focusGaind on TEXT(1) TAB KEY_RELEASED 1179812683265 on TEXT(1) 1 KEY_PRESSED 1179812683375 on TEXT(1) 1 KEY_TYPED 1179812683375 on TEXT(1) 1 KEY_RELEASED 1179812683453 on TEXT(1) TAB KEY_PRESSED 1179812683562 on TEXT(1) InputVerifier#verify on TEXT(1) TAB KEY_TYPED 1179812683562 on TEXT(1) TAB KEY_RELEASED 1179812683640 on TEXT(1) 1 KEY_PRESSED 1179812683765 on TEXT(1) 1 KEY_TYPED 1179812683765 on TEXT(1) 1 KEY_RELEASED 1179812683828 on TEXT(1) FocusListener#focusGaind on TEXT(2)
TextのInputVerifierにてwaitをかけた場合
JTextField, JTextField, JTextField を配置し、一つ目のJTextFieldのInputVerifier#verify にてwaitをかけます。
1つめのJTextFieldにフォーカスがある状態で、TAB, 1, TAB, 1, TAB, 1 のKeyを続けて押下
してテストを行いました。
各Componentにはそれぞれ、java.awt.event.FocusListener,
javax.swing.InputVerifier を設定しています。
- JDK1.5.0_08の場合
正常に動作します
操作orイベント KEYイベントの種類 getWhenの時間 処理Component FocusListener#focusGaind on TEXT(1) TAB KEY_PRESSED 1179812205140 on TEXT(1) InputVerifier#verify on TEXT(1) TAB KEY_TYPED 1179812205140 on TEXT(1) FocusListener#focusGaind on TEXT(2) TAB KEY_RELEASED 1179812205234 on TEXT(2) 1 KEY_PRESSED 1179812205375 on TEXT(2) 1 KEY_TYPED 1179812205375 on TEXT(2) 1 KEY_RELEASED 1179812205468 on TEXT(2) TAB KEY_PRESSED 1179812205593 on TEXT(2) InputVerifier#verify on TEXT(2) TAB KEY_TYPED 1179812205593 on TEXT(2) FocusListener#focusGaind on TEXT(3) TAB KEY_RELEASED 1179812205687 on TEXT(3) 1 KEY_PRESSED 1179812205812 on TEXT(3) 1 KEY_TYPED 1179812205812 on TEXT(3) 1 KEY_RELEASED 1179812205906 on TEXT(3) TAB KEY_PRESSED 1179812206062 on TEXT(3) InputVerifier#verify on TEXT(3) TAB KEY_TYPED 1179812206062 on TEXT(3) FocusListener#focusGaind on TEXT(4) TAB KEY_RELEASED 1179812206156 on TEXT(4) 1 KEY_PRESSED 1179812206312 on TEXT(4) 1 KEY_TYPED 1179812206312 on TEXT(4) 1 KEY_RELEASED 1179812206390 on TEXT(4)
- JDK1.5.0_10の場合
処理中の2回目のTABによるフォーカス移動が行われない。
最後の 1 のKEYは、TEXT(2)のInputVefirier#verifyが処理された後に、TEXT(2)に対して反映している。
そのため、InputVefirierではチェック漏れが発生するという不具合が発生する。
操作orイベント KEYイベントの種類 getWhenの時間 処理Component InputVerifier#verify on TEXT(0) FocusListener#focusGaind on TEXT(1) TAB KEY_PRESSED 1179811916437 on TEXT(1) InputVerifier#verify on TEXT(1) TAB KEY_TYPED 1179811916437 on TEXT(1) FocusListener#focusGaind on TEXT(2) TAB KEY_RELEASED 1179811916515 on TEXT(2) 1 KEY_PRESSED 1179811916672 on TEXT(2) 1 KEY_TYPED 1179811916672 on TEXT(2) 1 KEY_RELEASED 1179811916750 on TEXT(2) TAB KEY_PRESSED 1179811916937 on TEXT(2) InputVerifier#verify on TEXT(2) TAB KEY_TYPED 1179811916937 on TEXT(2) TAB KEY_RELEASED 1179811917031 on TEXT(2) 1 KEY_PRESSED 1179811917187 on TEXT(2) 1 KEY_TYPED 1179811917187 on TEXT(2) 1 KEY_RELEASED 1179811917250 on TEXT(2) TAB KEY_PRESSED 1179811917453 on TEXT(2) InputVerifier#verify on TEXT(2) TAB KEY_TYPED 1179811917453 on TEXT(2) TAB KEY_RELEASED 1179811917531 on TEXT(2) 1 KEY_PRESSED 1179811917718 on TEXT(2) 1 KEY_TYPED 1179811917718 on TEXT(2) 1 KEY_RELEASED 1179811917781 on TEXT(2) FocusListener#focusGaind on TEXT(3)
Webtribe,VisualFrameのRunClientの場合↑top
Button処理にてwaitをかけた場合
Button, TextField, TextField を配置し、Buttonにフォーカスがある状態で、SPACE, TAB, 1, TAB, 1 Keyを続けて押下 してテストを行いました。
RunClientの仕様として「処理中のフォーカス移動不可」があるため、 JDK1.5.0_10以上にすることによる動作差、不具合は発生しません。
- JDK1.5.0_09、JDK1.5.0_10
正常に動作します
操作orイベント KEYイベントの種類 getWhenの時間 処理Component enqueueKeyEvents on Button SPACE KEY_PRESSED 1179823004608 on Button SPACE KEY_TYPED 1179823004608 on Button SPACE KEY_RELEASED 1179823004702 on Button BeforeStart on Button BeforeEnd on Button AfterStart on Button [実行リスト処理開始] 1179823004703 AfterEnd on Button TAB KEY_PRESSED 1179823005202 on Button [requestFocus取消] TAB KEY_TYPED 1179823005202 on Button TAB KEY_RELEASED 1179823005296 on Button 1 KEY_PRESSED 1179823005405 on Button 1 KEY_TYPED 1179823005405 on Button 1 KEY_RELEASED 1179823005514 on Button TAB KEY_PRESSED 1179823005624 on Button [requestFocus取消] TAB KEY_TYPED 1179823005624 on Button TAB KEY_RELEASED 1179823005702 on Button 1 KEY_PRESSED 1179823005874 on Button 1 KEY_TYPED 1179823005874 on Button 1 KEY_RELEASED 1179823005952 on Button [実行リスト処理終了]
TextのAfterにてwaitをかけた場合
TextField, TextField, TextField を配置し、
1つめ、2つめ のTextのAfterにwaitをかける設定を行います。
1つめのTextFieldにフォーカスがある状態で TAB, 1, TAB, 1 とKEY入力してテストを行いました。
- JDK1.5.0_09
正常に動作します
操作orイベント KEYイベントの種類 getWhenの時間 処理Component TAB KEY_PRESSED 1179824346937 on text1 AfterStart on text1 [実行リスト処理開始] 1179824346937 AfterEnd on text1 enqueueKeyEvents on text2 BeforeStart on text2 BeforeEnd on text2 TAB KEY_TYPED 1179824346937 on text1 [実行リスト処理終了] 1179824351937 TAB KEY_RELEASED 1179824347015 on text2 1 KEY_PRESSED 1179824347109 on text2 1 KEY_TYPED 1179824347109 on text2 1 KEY_RELEASED 1179824347203 on text2 TAB KEY_PRESSED 1179824347281 on text2 AfterStart on text2 [実行リスト処理開始] 1179824351953 AfterEnd on text2 enqueueKeyEvents on text3 BeforeStart on text3 BeforeEnd on text3 TAB KEY_TYPED 1179824347281 on text2 [実行リスト処理終了] 1179824352953 TAB KEY_RELEASED 1179824347359 on text3 1 KEY_PRESSED 1179824347468 on text3 1 KEY_TYPED 1179824347468 on text3 1 KEY_RELEASED 1179824347547 on text3
■補足
処理順番と getWhenでの時間が一致していない (KeyEventの処理タイミングが遅れている) のは、Swing標準のKeyboardFocusManager の仕様のためです。
- JDK1.5.0_11
Swingハードコーディングと同様の不具合が発生します。
この操作を行った場合、 処理終了時はの text2のテキスト上の文字は 「11」となりますが、 text2のAfterのタイミングでは 「1」として処理が行われるため、画面での表示とアイテム内データ値とに 不整合が発生することになります。
操作orイベント KEYイベントの種類 getWhenの時間 処理Component TAB KEY_PRESSED 1179824465468 on text1 AfterStart on text1 [実行リスト処理開始] 1179824465484 AfterEnd on text1 enqueueKeyEvents on text2 BeforeStart on text2 BeforeEnd on text2 TAB KEY_TYPED 1179824465468 on text1 [実行リスト処理終了] 1179824470484 TAB KEY_RELEASED 1179824465578 on text2 1 KEY_PRESSED 1179824465656 on text2 1 KEY_TYPED 1179824465656 on text2 1 KEY_RELEASED 1179824465750 on text2 TAB KEY_PRESSED 1179824465859 on text2 AfterStart on text2 [実行リスト処理開始] 1179824470500 AfterEnd on text2 enqueueKeyEvents on text3 BeforeStart on text3 BeforeEnd on text3 TAB KEY_TYPED 1179824465859 on text2 TAB KEY_RELEASED 1179824465953 on text2 1 KEY_PRESSED 1179824466047 on text2 1 KEY_TYPED 1179824466047 on text2 1 KEY_RELEASED 1179824466125 on text2 [実行リスト処理終了] 1179824471500
設定方法↑top
[サンプル提供]:
jp.ne.mki.wedge.education.focusmanager.EnqueueNoWaitKeyboardFocusManager
のソースを各プロジェクト用にカスタマイズ(パッケージ名、クラス名、JavaDocなどを変更)し、
RunClient起動時のクラスパスにクラスを設定します。
RunClient.wdg(Config)ファイルの 「KeyboradFocusManagerの設定」 の keybordfocusmanagerのvalueに、クラス名を指定します。 このXML要素はデフォルトではコメントになっていますので、コメントから外します。
KeyboardFocusManagerを任意のクラスに置き換える場合は、 AWTPermission "replaceKeyboardFocusManager" のアクセス権が必要です。
JWS や Applet にて起動する場合には、署名を行うなどしてアクセス権を許可する環境にする必要があります。