2.3.2 Linux および Solaris 10 OS x86 のキーボード問題
2.6.2 AWT のクラッシュをトラブルシューティングする方法
2.7.4 X ウィンドウマネージャーでサポートされているフォーカスモデル
2.9.6 X11 上でポップアップメニューや類似コンポーネントをデバッグする際にハングアップする
この章では、Java SE AWT API で発生する可能性のあるもっとも一般的な問題のいくつかをトラブルシューティングするための、具体的な手順に関する情報と指針を提供します。
AWT のデバッグ時に役立つ可能性のあるヒントを次に示します。
sun.awt.noisyerrorhandler
システムプロパティーを true
に設定します。Java SE 6 以前では、NOISY_AWT
環境変数がこの目的で使用されていました。AWT コンポーネント階層をダンプするには、Ctrl+Shift+F1 を押します。
アプリケーションがハングアップする場合は、Solaris OS および Linux では Ctrl+\ (SIGQUIT)、Windows では Ctrl+Break を使ってスタックトレースを取得します。
Java SE 7 より前は、AWT のイベントディスパッチスレッド (EDT) でスローされた例外をキャッチするには、システムプロパティー sun.awt.exception.handler
に public void handle(Throwable)
メソッドを実装したクラスの名前を設定していました。Java SE 7 ではこのメカニズムが更新され、標準の Thread.UncaughtExceptionHandler
インタフェースが使用されるようになりました。
AWT の問題をデバッグする際には、ロガーによって生成される出力が役立ちます。ロガーの使用方法については、「Java ロギングの概要」や java.util.logging
パッケージの説明を参照してください。
使用可能なロガーは次のとおりです。
このセクションでは、発生する可能性のあるいくつかのレイアウト問題について説明し、回避方法がある場合はその方法を提供します。
原因: レイアウトマネージャー GridBagLayout のある特性のために、ipadx または ipady が設定された状態で invalidate() と validate() が呼び出されると、コンポーネントのサイズが ipadx または ipady の値まで増えます。その理由は、コンテナ内にコンポーネントを格納するために必要な領域の量を、レイアウトマネージャー GridBagLayout が繰り返し計算するからです。
回避方法: JDK には、そのような場合にレイアウトマネージャーがコンポーネントを配置し直すべきかどうかを検出するための信頼できる単純な方法は用意されていませんが、非常に単純な回避方法があります。getPreferredSize() メソッドをオーバーライドしたコンポーネントを使用してください。このメソッドは常に現在の必要サイズを返します。
public Dimension getPreferredSize(){ return new Dimension(size+xpad*2+1, size+ypad*2+1); }
原因: Container.doLayout() メソッドから validate() を呼び出すと、AWT 自身が validate() から doLayout() を呼び出すため、無限再帰が発生する場合があります。
このセクションではキーイベントの問題について説明します。
現在解決されていないキーボードの問題は、次のとおりです。
一部の非英語キーボードではキートップに特定のアクセントキーが刻印されており、したがってそれらはプライマリレイヤーの文字です。ところが、それらに対応する Java キーコードがないため、ニーモニックとして使用できません。
実行時にデフォルトのロケールを変更しても、メニューのアクセラレータキーとして表示されるテキストが変更されません。
標準 109 キー日本語キーボードでは、円記号キーとバックスラッシュキーのどちらの場合もバックスラッシュが生成されますが、これは両者の WM_CHAR メッセージの文字コードが同じだからです。AWT はそれらを区別すべきです。これは将来のリリースで修正される予定です。
Linux および Solaris 10 OS x86 システムに関係するキーボードの問題を次に示します。
これらのシステムでのキーボード入力は通常、X Window の XKEYBOARD 拡張に基づいています。ユーザーは、1 つのキーボードレイアウト (例: デンマーク語 dk) のみを構成することも、いくつかのレイアウト (例: us と dk) を構成して切り替えることもできます。
sk、hu、cz などの一部のキーボードレイアウトでは、数値入力キーパッドの小数点を押したときに、小数点の入力だけでなく 1 つ前の文字の削除も行われます。その原因はネイティブのバグにあります。回避方法は、us と sk など、2 つのレイアウトを使用することです。この場合、どちらのレイアウトでも数値入力キーパッドは正しく動作します。
キーボードの動的変更をサポートする UNIX システム上で、実行中の Java アプリケーションはそのような変更を認識しません。たとえば、キーボードを US からドイツ語に変更しても、キーボードマッピングは変更されません。X サーバーはその変更を検出して対象クライアントに MappingNotify イベントを送出しますが、AWT はキーコード - キーシムマッピングの情報をリフレッシュしません。
Java SE 6 リリースでは、AWT モーダリティーの領域で多くの問題が解決され、多くの改善が実装されました。Java SE 1.5 以前のリリースでモーダリティーの問題が発生している場合は、まず最新の Java SE リリースにアップグレードし、問題がすでに修正されているか確認してください。Java SE 6 で修正された問題のいくつかを次に示します。
モーダルダイアログがブロックされたフレームの背後に移動します。
同じウィンドウを親に持つ 2 つのモーダルダイアログが同時に開いていました。
一部の Solaris OS または Linux 環境 (CDE ウィンドウマネージャーを使用する場合など) では、モーダリティーの改善点の多くは使用できません。Java SE 6 以降で、あるモーダリティータイプやモーダル除外タイプが特定の構成でサポートされているかどうかを確認するには、メソッド Toolkit.isModalityTypeSupported() および Toolkit.isModalExclusionTypeSupported() を使用します。
Solaris OS または Linux で Java モーダルダイアログを実行する場合に別の問題があります。モーダルダイアログが画面に表示される際に、ウィンドウマネージャーによって、同じアプリケーション内のトップレベルの Java ウィンドウのいくつかがタスクバーに表示されなくなる場合があります。これによってエンドユーザーが困惑する可能性もありますが、ユーザーの作業にはさほど影響はありません。非表示になったウィンドウはすべてモーダルブロックの状態になっており、操作できないからです。
アプリケーションをブラウザ内のアプレットとして実行してモーダルダイアログを表示した場合、ブラウザのウィンドウがブロックされる可能性があります。このブロック処理の実装は、ブラウザやオペレーティングシステムごとに異なります。たとえば、Windows では Internet Explorer と Mozilla はどちらも正しく動作しますが、Solaris OS や Linux では Mozilla のウィンドウはブロックされません。これは将来のリリースで修正される予定です。
Java SE 7 向けの AWT モーダリティードキュメントでは、モーダリティー関連の機能とその使用方法について説明しています。このドキュメントのセクションの 1 つでは、モーダルダイアログに関係したりその影響を受けたりする可能性のある領域 (常時最前面プロパティー、フォーカス処理、ウィンドウ状態など) をいくつか説明しています。そのような場合のアプリケーションの動作は通常、未定義となるかプラットフォームに依存するので、特定の動作をあてにしないでください。
このセクションではまず、メモリーリークのトラブルシューティングの方法について説明します。その後、考えられるメモリーリークの原因をいくつか示し、回避方法を説明します。
メモリーリークの詳細情報を得るには、java の実行時にヒーププロファイラをアクティブにします。jhat ユーティリティーを使って出力を読み取れるように、出力をバイナリ形式で生成するよう指定します。
$ java -agentlib:hprof=file=snapshot.hprof,format=b application
メモリーリークのトラブルシューティングの詳細情報や、jhat ユーティリティーやその他の使用可能なトラブルシューティングツールの説明については、HotSpot VM に関する Java SE 7 向けのトラブルシューティングガイドを参照してください。
問題: アプリケーション内でのメモリーリーク。
原因: フレームやダイアログのガベージコレクションが行われないときがあります。このバグは Java SE の将来のバージョンで修正される予定です。
回避方法: 既知のメモリーリークは、あるフォーカス可能なトップレベル要素 (ウィンドウ、ダイアログ、フレーム) へのフォーカス移動がシステムによって開始されたが、フォーカス移動が完了する前にその要素がクローズ、非表示化、または破棄されてしまった場合に発生します。したがってアプリケーションは、フォーカス移動操作が完了するまで待ってから要素のクローズ、非表示化、または破棄を行う必要があります。
この問題は通常、これらのアクションがプログラム経由で実行される場合にのみ発生します。通常、問題が発生する速度でユーザーがこれらのアクションを実行することは、身体的に不可能だからです。
このセクションでは、クラッシュが AWT に関係するかどうかを判断する方法や、そのようなクラッシュをトラブルシューティングする方法を説明します。
クラッシュが発生すると、致命的エラーの発生時に取得された情報や状態を含むエラーログが作成されます。このログファイルの詳細については、「付録 B: 致命的エラーログ」を参照してください。
このファイルの先頭付近の行には、エラーが発生したライブラリが示されます。下の例では、クラッシュが AWT ライブラリに関係していたことがわかります。
... # Java VM: Java HotSpot(TM) Client VM (1.6.0-beta2-b76 mixed mode, sharing) # Problematic frame: # C [awt.dll+0x123456] ...
ただし、システムライブラリ内のどこか深い場所でクラッシュが発生しても、その原因がやはり AWT である場合もあります。その場合、awt.dll という目印は、問題のあるフレームとしては現れないため、ファイル内のセクション Stack: Native frames: Java frames をさらに確認する必要があります。次に例を示します。
Stack: [0x0aeb0000,0x0aef0000), sp=0x0aeefa44, free space=254k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C 0x00abc751 C [USER32.dll+0x3a5f] C [USER32.dll+0x3b2e] C [USER32.dll+0x5874] C [USER32.dll+0x58a4] C [ntdll.dll+0x108f] C [USER32.dll+0x5e7e] C [awt.dll+0xec889] C [awt.dll+0xf877d] j sun.awt.windows.WToolkit.eventLoop()V+0 j sun.awt.windows.WToolkit.run()V+69 j java.lang.Thread.run()V+11 v ~StubRoutines::call_stub V [jvm.dll+0x83c86] V [jvm.dll+0xd870f] V [jvm.dll+0x83b48] V [jvm.dll+0x838a5] V [jvm.dll+0x9ebc8] V [jvm.dll+0x108ba1] V [jvm.dll+0x108b6f] C [MSVCRT.dll+0x27fb8] C [kernel32.dll+0x202ed] Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) j sun.awt.windows.WToolkit.eventLoop()V+0 j sun.awt.windows.WToolkit.run()V+69 j java.lang.Thread.run()V+11 v ~StubRoutines::call_stub
ネイティブフレーム内のどこかにテキスト awt.dll が現れた場合、そのクラッシュは AWT に関係している可能性があります。
AWT クラッシュの大部分は Windows プラットフォーム上で発生しますが、それらの原因はスレッドの競合です。これらの問題の多くが Java SE version 6 で修正されたので、以前のリリースでクラッシュが発生した場合はまず、その問題がすでに最新リリースで修正されているか確認してみてください。
考えられるクラッシュの原因の 1 つは、AWT の多くの操作が非同期であることです。たとえば、frame.setVisible(true) を呼び出してフレームを表示させる場合、この呼び出しから戻ったあとでそのフレームがアクティブウィンドウになっている保証はありません。
もう 1 つの例は、ネイティブのファイルダイアログに関するものです。オペレーティングシステムがこれらのダイアログを初期化して表示するまで多少の時間がかかるため、setVisible(true) を呼び出してすぐにそれらを破棄してしまうと、クラッシュが発生する可能性があります。したがって、アプリケーション内にいくつかの AWT 呼び出しがあり、それらが同時に実行されたり短時間に連続して実行されたりする場合には、呼び出しの間にある程度の遅延を挿入したり、何らかの同期を追加したりすることをお勧めします。
このセクションには次の情報が含まれます。
フォーカスイベントをトレースする方法
プラグイン内のフォーカスシステムの説明
X ウィンドウマネージャーでサポートされているフォーカスモデル
フォーカストラバーサル
発生する可能性のあるその他のフォーカス問題
フォーカスの問題をトラブルシューティングするには、フォーカスイベントをトレースします。まず次に示すように、単純にフォーカスリスナーをツールキットに追加します。
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener( public void eventDispatched(AWTEvent e) { System.err.println(e); } ), FocusEvent.FOCUS_EVENT_MASK | WindowEvent.WINDOW_FOCUS_EVENT_MASK | WindowEvent.WINDOW_EVENT_MASK);
ここで System.err ストリームが使用されているのは、出力のバッファリングが行われないからです。
なお、フォーカスイベントの正しい順序は次のとおりです。
フォーカスを失うコンポーネントの FOCUS_LOST
フォーカスを失うトップレベルの WINDOW_LOST_FOCUS
アクティブ状態を失うトップレベルの WINDOW_DEACTIVATED
アクティブウィンドウになるトップレベルの WINDOW_ACTIVATED
フォーカスされたウィンドウになるトップレベルの WINDOW_GAINED_FOCUS
フォーカスを得るコンポーネントの FOCUS_GAINED
フォーカスされたウィンドウ内のコンポーネント間でフォーカスが移動する場合は、FOCUS_LOST イベントと FOCUS_GAINED イベントのみが生成されるはずです。同じ所有者が所有するウィンドウ間や、所有されるウィンドウとその所有者の間でフォーカスが移動する場合は、次のイベントが生成されるはずです。
FOCUS_LOST
WINDOW_LOST_FOCUS
WINDOW_GAINED_FOCUS
FOCUS_GAINED
フォーカスを失うイベントやアクティブ化のイベントが先に発生するはずです。
時折、ネイティブプラットフォームが原因で問題が発生することがあります。これをチェックするには、フォーカスに関係するネイティブイベントを調査します。フォーカス対象のウィンドウがアクティブ化され、フォーカス対象のコンポーネントがネイティブフォーカスイベントを受け取ることを確認してください。
Windows プラットフォームのネイティブフォーカスイベントは、次のとおりです。
WM_ACTIVATE (トップレベル用)。WPARAM は、アクティブ化の際は WA_ACTIVE、非アクティブ化の際は WA_INACTIVE になります。
WM_SETFOCUS および WM_KILLFOCUS (コンポーネント用)。
Windows プラットフォームでは「合成フォーカス」という概念が実装されています。つまり、フォーカス所有者コンポーネントはそのフォーカス可能状態をエミュレートするだけで、実際のネイティブフォーカスは「フォーカスプロキシ」コンポーネントに設定されます。このコンポーネントは、キーとインプットメソッドのネイティブメッセージを受け取り、それらをフォーカス所有者にディスパッチします。JDK7 より前は、フォーカスプロキシコンポーネントはフレーム/ダイアログ内の専用の非表示の子コンポーネントでした。JDK7 では、フレーム/ダイアログ自体がフォーカスプロキシとして機能します。現在は、所有されるウィンドウ内のコンポーネントだけでなく、すべての子コンポーネントのフォーカスプロキシとしても機能します。単純なウィンドウは、ネイティブフォーカスを受け取ることは決してなく、その所有者のフォーカスプロキシに依存します。ユーザーがこのメカニズムを意識することはありませんが、デバッグ時には考慮に入れるべきです。
Solaris OS および Linux の XToolkit では、AWT がフォーカスを自分で管理することを可能にするフォーカスモデルが使用されます。このモデルでは、ウィンドウマネージャーが直接トップレベルウィンドウに入力フォーカスを設定することはなく、代わりに WM_TAKE_FOCUS クライアントメッセージのみを送信することで、フォーカスを設定すべきであることを示します。その後、トップレベルウィンドウへのフォーカス設定が可能な場合は、その設定が AWT によって明示的に行われます。
X サーバーや一部のウィンドウマネージャーからウィンドウにフォーカスイベントが送信される可能性もあります。ただし、そのようなイベントはすべて AWT によって破棄されます。
トップレベル内のコンポーネントがフォーカスを得ても、AWT はフォーカスイベントの階層チェーンを生成しません。さらに、コンポーネント自体にマップされたネイティブウィンドウがネイティブフォーカスイベントを取得することはありません。Solaris OS および Linux プラットフォームでは Windows プラットフォームと同じく、AWT はフォーカスプロキシメカニズムを使用します。したがって、コンポーネントのフォーカスはフォーカスイベントの合成によって設定され、不可視のフォーカスプロキシがネイティブフォーカスを保持します。
Window オブジェクト (Frame オブジェクトでも Dialog オブジェクトでもない) にマップされたネイティブウィンドウには、override-redirect フラグが設定されます。したがって、ウィンドウマネージャーはそのウィンドウに対し、フォーカス移動に関して通知しません。このウィンドウでフォーカスが要求されるのは、マウスクリックへの応答としてだけです。このウィンドウはネイティブフォーカスイベントを一切受け取りません。したがって、トレース可能なイベントは、フレームまたはダイアログ上の FocusIn または FocusOut イベントだけです。XToolkit では、フォーカスの主な処理が Java レベルで発生するため、フォーカスのデバッグが WToolkit の場合よりも単純になります。
アプレットは、EmbeddedFrame の子 (ただし直接の子ではない) としてブラウザ内に埋め込まれます。これは、プラグインとの通信機能を備えた特殊な Frame です。アプレットからは、EmbeddedFrame はトップレベルの完全な Frame に見えます。EmbeddedFrame のフォーカスを管理するには、特殊な追加アクションが必要になります。アプレットが最初に起動する際に、EmbeddedFrame はネイティブシステムによってデフォルトでアクティブ化されません。アクティブ化は、EmbeddedFrame に用意された特殊な API をプラグインがトリガーすることによって実行されます。フォーカスがアプレットを離れる際の EmbeddedFrame の非アクティブ化も、合成された方法で行われます。
X ウィンドウマネージャーでサポートされているフォーカスモデルは、次のとおりです。
click-to-focus は一般的に使用されているフォーカスモデルです。(たとえば、Microsoft Windows ではこのモデルが使用されています。)
focus-follows-mouse は、マウスの下にあるウィンドウにフォーカスが移動するフォーカスモデルです。
Java SE 7 の XAWT では focus-follows-mouse モードは検出されませんが、これにより、単純なウィンドウ (java.awt.Window クラスのオブジェクト) で問題が発生します。そのようなウィンドウには override-redirect プロパティーが設定されているため、ウィンドウにフォーカスを移動できるのはマウスボタンを押した場合だけであり、ウィンドウの上にマウスを移動してもフォーカスは移動しません。回避方法としては、ウィンドウに MouseListener を設定し、マウスがウィンドウのボーダーを横切ったときにウィンドウへのフォーカスを要求します。
このセクションでは、AWT で発生する可能性のあるいくつかのフォーカス問題を説明し、その解決方法を提示します。
フレームの内側にあるコンポーネントをクリックすると、フォーカスが変更されます。
解決方法: ウィンドウマネージャーのバージョンをチェックし、3.0 以上にアップグレードします。
解決方法: トラバーサルキーイベントをキャッチするには、Component.setFocusTraversalKeysEnabled(boolean) を呼び出してそれらのイベントを有効にする必要があります。
その所有者であるフレームはモーダルブロックされています。この場合、ウィンドウもモーダルブロックされたままになります。
解決方法: ウィンドウは、その所有者がフォーカスの取得を許可されていない場合はフォーカスされたウィンドウになれません。解決方法は、所有者をモーダリティーから除外することです。
java.lang.NullPointerException: null pData がスローされる場合があります。
解決方法: 例外のスローを避けるもっとも簡単な方法は、削除とフォーカス要求を EDT 上で行うことです。もう 1 つのより複雑なアプローチは、フォーカス要求と削除を同期することです (これらのアクションをそれぞれ異なるスレッド上で実行する必要がある場合)。
たとえば、コンポーネント A がフォーカス所有者とします。コンポーネント B がフォーカスを要求し、その直後にコンポーネント A がそのコンテナから削除されます。フォーカスは最終的に、コンテナ内でコンポーネント A のあとに配置されたコンポーネント C に移動し、コンポーネント B には移動しません。
解決方法: この場合、フォーカス要求がコンポーネント A の削除前ではなく削除後に実行されるようにしてください。
たとえば、フレームが 1 つと、そのフレームが所有するウィンドウが 1 つ表示されているとします。フレームがアクティブでないため、ウィンドウはフォーカスされません。その後、ウィンドウを alwaysOnTop に設定します。ウィンドウはフォーカスを取得しますが、その所有者は非アクティブなままです。したがって、ウィンドウはキーイベントを受け取れません。
解決方法: ウィンドウを alwaysOnTop に設定する前に、フレームを前面に移動します (Frame.toFront() メソッド)。
解決方法: フレームを表示したあとで (Frame.setVisible(true) メソッド)、フレームを前面に移動します (Frame.toFront() メソッド)。
たとえば、あるフレームがフォーカスされたウィンドウであり、そのコンポーネントの 1 つがフォーカス所有者であるとします。別のウィンドウがクリックされたあと、このフレームが再度クリックされます。WINDOW_GAINED_FOCUS がフレームに送信され、WindowFocusListener.windowGainedFocus(WindowEvent) メソッドが呼び出されます。ただし、このコールバックの内部では、Frame.getMostRecentFocusOwner() から null が返されるため、フレームの直近のフォーカス所有者を確認できません。
解決方法: WindowListener.windowActivated(WindowEvent) コールバックの内部では、フレームの直近のフォーカス所有者を取得できます。ただし、この時点までにフレームがフォーカスされたウィンドウになっているのは、フレームが所有するウィンドウが 1 つも存在しない場合だけです。このアプローチは、ウィンドウでは機能せず、フレームまたはダイアログでのみ機能します。
解決方法: この動作は、JDK 1.3 以降のデフォルト動作です。ただし、アプレットが起動時にフォーカスを取得しないようにする必要がある場合もあります (アプレットが不可視であるためにフォーカスがまったく不要な場合など)。この場合は次のように、HTML タグ内で特殊パラメータ initial_focus を false に設定できます。
<applet code="MyApplet" width=50 height=50> <param name=initial_focus value="false"> </applet>
解決方法: Component.setEnabled(false) または Component.setFocusable(false) を呼び出すことで設定された状態が、そのすべてのコンテンツでもフォーカス不可能な状態として維持されるとは仮定しないでください。代わりに、Window.setFocusableWindowState(boolean) メソッドを使用します。
このセクションでは、ドラッグ&ドロップやクリップボードで発生する可能性のある問題について説明します。
デバッガを使ってドラッグ&ドロップをトラブルシューティングすることは困難です。ドラッグ&ドロップの操作中はすべての入力が占有されるからです。したがって、ドラッグ&ドロップ中にブレークポイントを設定した場合には、X サーバーを再起動しなければいけない可能性があります。代わりにリモートデバッグを使用してみてください。
ドラッグ&ドロップに関するほとんどの問題のトラブルシューティングに使用可能な単純な方法が 2 つあります。
すべての DataFlavor インスタンスの出力
受信されたデータの出力
リモートデバッグに代わる方法として、遅延なしで出力する System.err.println() 関数があります。
このセクションでは、AWT のドラッグ&ドロップで頻繁に発生するいくつかの問題を説明し、トラブルシューティングの方法を提案します。
Clipboard.getContents() 関数を使ってペースト操作を行うと、アプリケーションが少しの間、ハングアップすることがあります (特にペースト対象データの提供元がリッチアプリケーションである場合)。
Clipboard.getContents() 関数は、使用可能なすべてのフレーバー (いくつかのテキストフレーバーやイメージフレーバーなど) に含まれるクリップボードデータを取得しますが、これはコストが高く、不要である可能性があります。
解決方法: Clipboard.getData() メソッドを使って特定のデータのみをクリップボードから取得します。1 つのフレーバーまたは少数のフレーバーのみのデータが必要な場合は、getContents() の代わりに次のいずれかの Clipboard メソッドを使用してください。
DataFlavor[] getAvailableDataFlavors()
boolean isDataFlavorAvailable(DataFlavorflavor)
Object getData(DataFlavorflavor)
転送データの初期化を必要な場合にのみ行えるよう、Transferable.getTransferData() 内に初期化コードが配置されました。
Transferable データの生成コストは高く、DnD 操作中に Transferable.getTransferData() が複数回呼び出されるため、速度が低下します。
解決方法: Transferable データが一度だけ生成されるように、このデータをキャッシュします。
Windows や一部のウィンドウマネージャーでは、転送ファイルのリストは DataFlavor.javaFileListFlavor データフレーバーとして表現できます。しかし、すべてのウィンドウマネージャーがファイルのリストをこの形式で表現するわけではありません。たとえば、GNOME ウィンドウマネージャーは、ファイルリストを URI のリストとして表現します。
回避方法: ファイルを取得するには、String 型のデータを要求したあと、RFC 2483 で説明されている text/uri-list 形式に従って文字列をファイルのリストに変換します。Java アプリケーションから GNOME/KDE デスクトップやファイルブラウザにファイルをドロップできるようにするには、text/uri-list 形式でデータをエクスポートします。コード例については、次のバグレポートの Work Around のセクションを参照してください。
解決方法: DnD 操作中のマウスカーソルの動きに合わせて、表面にイメージがレンダリングされたウィンドウを移動します。次の RFE の Work Around のセクションのコード例を参照してください。
DataFlavor クラスには、配列を処理するコンストラクタはありません。配列の MIME タイプには、エスケープすべき文字が含まれます。たとえば、次のコードでは IllegalArgumentException がスローされます。
new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + "; class=" + (new String[0]).getClass().getName())
解決方法: 次のコードに示すように、表現クラスのパラメータの値を引用符で囲みます (引用符がエスケープされている)。
new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + "; class=" + "\"" + (new String[0]).getClass().getName() + "\"")
詳細については、次のバグレポートを参照してください。
DnD 操作時に奇妙なイベントが発生する、複数の項目をドラッグ&ドロップできない、InvalidDnDOperationException がスローされるなど、さまざまな問題が発生する可能性があります。
解決方法: Swing コンポーネントでは Swing の DnD サポートを使用します。Swing の DnD 実装は AWT の DnD 実装に基づいていますが、Swing と AWT のドラッグ&ドロップを混在させることはできません。次のドキュメントを参照してください。
ターゲットに依存するようにソースの状態を変更するには、ソースコンポーネントとターゲットコンポーネントへの参照が同じコード領域内で必要になりますが、これは現時点ではドラッグ&ドロップ API に実装されていません。
回避方法: 回避方法の 1 つは、イベントのコンテキストを判定できるようにするためのフラグを、転送可能オブジェクトに追加することです。
1 つの Java VM 内でのデータ転送では、次の回避方法を提案します。
ターゲットコンポーネントを DragSourceListener として実装します。
DragGestureRecognizer.dragGestureRecognized() 内で次のように、ターゲットをドラッグソースリスナーとして追加します。
public void dragGestureRecognized(DragGestureEvent dge) { dge.startDrag(null, new StringSelection("SomeTransferedText")); dge.getDragSource().addDragSourceListener(target); }
これで、DragSourceListener() の dragEnter()、dragOver()、dropActionChanged()、dragDropEnd() の各メソッド内で、ターゲットとソースを取得できます。
大量データの転送や転送オブジェクトの作成にかかる時間が長すぎます。ユーザーは、データ転送が完了するまで長時間待つ必要があります。
この高コスト処理で転送に時間がかかりすぎるのは、Transferable.getTransferData() が終了するまで待つ必要があるからです。
解決方法: この解決方法が有効なのは、1 つの Java VM 内でデータを転送する場合だけです。ドラッグ操作の前に、高コストのリソースを作成または取得しておきます。たとえば、Transferable.getTransferData() の処理時間が長くなりすぎないよう、転送可能オブジェクトの作成時にファイルのコンテンツを取得します。
このセクションでは、AWT のトラブルシューティングに関するその他の問題について説明します。
このセクションでは、Java SE 7 リリースの AWT のスプラッシュ画面で発生する可能性のあるいくつかの問題を説明し、その解決方法を提示します。
解決方法: 次の解決方法を参照してください。
解決方法: スプラッシュ画面のイメージが JAR ファイルから選択されるのは、-jar コマンド行オプションで JAR ファイルが使用された場合だけです。この JAR ファイルには、「SplashScreen-Image」マニフェストオプションとイメージファイルの両方を含めるべきです。-classpath の JAR ファイルの MANIFEST.MF 内でスプラッシュ画面がチェックされることは決してありません。-jar を使用しない場合でも、コマンド行で -splash を使ってスプラッシュ画面のイメージを指定できます。
解決方法: これは、X11 のネイティブの制限です。Solaris OS および Linux では、半透明イメージのアルファチャネルが 50% のしきい値と比較されます。アルファ値が 0.5 より高いピクセルは不透明に、アルファが 0.5 より低いピクセルは完全に透明になります。半透明のサポートは、Java SE の将来のバージョンで改善される可能性があります。
Windows 98 プラットフォームの Java SE 6 リリースではメソッド TrayIcon.displayMessage() はサポートされませんが、これは、Windows 98 ではバルーン表示用のネイティブサービスがサポートされていないからです。
SecurityManager がインストールされている場合、TrayIcon オブジェクトを作成するには、AWTPermission の値が accessSystemTray に設定される必要があります。
JPopupMenu.setInvoker() メソッドで、呼び出し元 (invoker) とは、ポップアップメニューの表示先となるコンポーネントのことです。このプロパティーが null に設定されていると、ポップアップメニューが正しく機能しません。
解決方法は、ポップアップの呼び出し元を自分自身に設定することです。
多くの AWT コンポーネントでは、バックグラウンドカラーやフォアグラウンドカラーとして、親のカラーではなく独自のデフォルトが使用されます。
この動作はプラットフォームに依存します。同じコンポーネントの動作がプラットフォームごとに異なる可能性があります。さらに一部のコンポーネントでは、バックグラウンドカラーまたはフォアグラウンドカラーの一方でデフォルト値が使用され、他方で親の値が選択されます。
アプリケーションの一貫性をすべてのプラットフォームで確保するには、すべてのコンポーネントやコンテナで、明示的なカラー割り当て (フォアグラウンドとバックグラウンドの両方) を使用してください。
AWT の Container にはサイズの制限があります。ほとんどのプラットフォームでは、この制限は 32767 ピクセルです。したがって、たとえばキャンバスオブジェクトの高さが 25 ピクセルであれば、Java AWT パネル上に約 1400 個を超える数のオブジェクトを表示することはできません。
残念ながら、Java コードやネイティブコードを使ってこの制限を変更する方法はありません。この制限は、オペレーティングシステムでウィジェットサイズの格納に使用されるデータの型に依存します。たとえば、Windows 2000/XP オペレーティングシステムや Linux オペレーティングシステムでは integer 型が使用されるため、整数の最大サイズが制限値となります。ほかのオペレーティングシステムでは、long など別の型が使用されている可能性があり、この場合であれば制限は高くなります。
詳細については、プラットフォームのドキュメントを参照してください。
役立つ可能性のあるこの制限の回避方法の例を次に示します。
コンポーネントをページ単位で表示します。
タブを使って一度に表示されるコンポーネントの数を減らします。
特定の GUI アクション (ポップアップメニューのナビゲーションなど) では、アクションの終了タイミングを判定できるようにすべての入力イベントを占有する必要があります。占有が続いている間は、ほかのアプリケーションは入力イベントを一切受け取りません。Java アプリケーションのデバッグ時に、占有が継続している間にブレークポイントに達すると、オペレーティングシステムがハングアップしたようになります。これは、占有を保持している Java アプリケーションがデバッガによって停止され、入力イベントを一切処理できくなったからであり、このインストールされた占有のために、ほかのアプリケーションは単純にイベントを受け取りません。そのようなアプリケーションのデバッグを可能にするには、デバッガからアプリケーションを実行する際に次のシステムプロパティーを設定するようにしてください。
-Dsun.awt.disablegrab=true
これにより事実上、占有の設定がオフになるので、システムがハングアップしなくなります。ただし、このオプションが設定されていると、場合によっては、通常であれば終了するはずの GUI アクションが終了できなくなります。たとえば、ウィンドウのタイトルバーをクリックしてもポップアップメニューが終了しない可能性があります。
Window.toFront()
/toBack()
の動作サードパーティーソフトウェア (特に Metacity などのウィンドウマネージャー) から課される制限のため、toFront()
/toBack()
メソッドは必ずしも予想どおりに機能せず、ほかのトップレベルウィンドウとの関係におけるウィンドウのスタック順序が変更されない可能性があります。詳細については、CR 6472274 を参照してください。
アプリケーションがあるウィンドウを最終的に最前面に移動させたい場合、この問題を回避するために、Window.setAlwaysOnTop(true)
を呼び出してそのウィンドウを一時的に「常時最前面」にしたあと、setAlwaysOnTop(false)
を呼び出して「常時最前面」状態をリセットすることができます。この回避方法も成功が保証されているわけではありません。将来ウィンドウマネージャーによってより多くの制限が課される可能性もあるからです。さらに、ウィンドウを「常時最前面」に設定できるのは、信頼できるアプリケーションだけです。サンドボックス内で実行される未署名のアプレットや Web Start アプリケーションは、この API を使用できず、したがって問題を回避できません。
ただし、ネイティブアプリケーションも似た問題に悩まされているので、この特殊性により、Java アプリケーションの動作はネイティブアプリケーションと似たものになっています。したがって、この問題はバグとはみなせません。
このセクションでは、重量/軽量 (HW/LW) 混在機能で発生する可能性のある問題について説明します。
コンポーネントのレイアウト関係のプロパティー (サイズ、位置、フォントなど) を 1 つでも変更すると、コンポーネントとその祖先が無効にされます。HW/LW 混在機能が正しく機能するには、そのような変更を行なったあとでコンポーネント階層を有効にする必要があります。無効化はデフォルトで、階層内の最上位コンテナ (Frame
オブジェクトなど) で停止します。したがって、階層の有効性を復元するには、アプリケーションから Frame.validate()
メソッドを呼び出すようにしてください。例:
component.setFont(myFont); frame.validate();
ここで、frame
は component
を含んでいるフレームのことです。Swing アプリケーションや Swing ライブラリ自身では、よく次のパターンが使用されます。
component.setFont(myFont); component.revalidate();
revalidate()
呼び出しは十分ではありません。階層内でコンポーネントのもっとも近い有効なルートからしか有効化が行われず、したがってそれより上のコンテナは無効なままになるからです。その場合、HW/LW 機能によって計算される重量コンポーネントの形状が正しくない可能性があり、画面上に視覚的なアーティファクトが表示される可能性があります。
コンポーネント階層全体の有効性を検証するために、ユーザーは、このドキュメントの 2.1 で説明されているキーの組み合わせ Ctrl+Shift+F1 を使用できます。コンポーネントに「invalid」というマークが付いている場合、validate()
呼び出しがどこかで欠落していることを示している可能性があります。
2.10.1 で言及した有効なルートの概念は Swing で導入されたものですが、その目的は、非常に長い時間がかかる可能性のあるコンポーネント階層の有効化処理を高速化するためでした。そのような最適化を行うと階層の上側の部分は無効なままになりますが、問題はありません。有効なルートの内側にあるコンポーネントのレイアウトは、外側のコンポーネント階層 (つまり有効なルートの兄弟) のレイアウトには影響しないからです。ただし、HW コンポーネントと LW コンポーネントが階層内で一緒に混在している場合、この文は正しくありません。それが、この機能でコンポーネント階層全体が有効でなければいけない理由です。
frame.validate()
の呼び出しも効率的ではない可能性があるため、AWT では、コンポーネント階層の無効化/有効化を処理するための最適化された代替手段がサポートされています。この機能を有効にするには、システムプロパティーを使用します。
-Djava.awt.smartInvalidate=true
このプロパティーが指定されると、invalidate()
メソッドは、invalidate()
メソッドを呼び出したコンポーネントのもっとも近い有効なルートに達した時点で、階層の無効化を停止するようになります。その後、アプリケーションは次を単純に呼び出すようにしてください。
component.revalidate();
これで、コンポーネント階層の有効性が復元されます。この場合、フレームはまだ有効であるため、frame.validate()
の呼び出しは事実上無操作になります。一部のアプリケーションは、階層の有効なルート (フレームなど) より上側にあるコンポーネントで直接 validate()
を呼び出すことに依存しているため、この新しい最適化された動作によって非互換性の問題が発生する可能性があり、したがってこの動作を使用できるのはシステムプロパティーを指定した場合だけです。
この最適化された新しいモードでアプリケーションを実行する際に何らかの問題が発生する場合、ユーザーは、このドキュメントの 2.1 で説明したキーの組み合わせ Ctrl+Shift+F1 を使用することで、コンポーネント階層のどの部分が無効なままになっており、したがって問題の原因となっている可能性があるのかを調査できます。
デフォルトでは、Swing ライブラリはコンポーネント階層内に重量コンポーネントは 1 つも存在しないものと仮定しているため、最適化された描画テクニックを使って Swing UI のパフォーマンスを向上させます。コンポーネント階層に HW コンポーネントが含まれている場合は、最適化をオフにする必要があります。これにはまず Swing の JScrollPane
が該当します。スクロールモードを変更するには、JViewPort.setScrollMode(int)
メソッドを使用します。
HW/LW 混在機能実装ではデフォルトで、不透明でない軽量コンポーネントはサポートされません。矩形以外の LW コンポーネントを HW コンポーネントと混在できるようにするには、アプリケーションで非公開 API の com.sun.awt.AWTUtilities.setComponentMixingCutoutShape()
を使用する必要があります。
ただし、矩形でない LW コンポーネントは、引き続き不透明 (アルファ == 1.0) または透明 (アルファ == 0.0) のいずれかのカラーを使用して自身をペイントするようにしてください。半透明カラー (0.0 < アルファ < 1.0) の使用はサポートされません。
一部の開発者は以前、HW および LW コンポーネントを一緒に混在させなければいけない場合のために、独自のサポートを実装しました。JDK 6 Update 12 および JDK 7 以降で使用可能な機能の組み込み実装が、カスタムの回避方法との間で問題を発生させる可能性があります。この組み込み機能を無効にするには、アプリケーションの起動時に次のシステムプロパティーを指定する必要があります。
-Dsun.awt.disableMixing=true