こんにちは、私はJava 1.6でSwingを使用してGUIアプリケーションを作成しています。
SwingのGUIの読み込み中と少し後に、gifアニメーションを表示するポップアップ画面があります。
私のポップアップ画面はJDialogです。次の方法でJdialogに追加されたJLabelにアニメーションを表示する必要があります。
ImageIcon myImgIcon = getMyImgIcon();
JLabel imageLbl = new JLabel(myImgIcon);
add(imageLbl, BorderLayout.CENTER);
ここで重要なのは、アニメーションが表示されるのはGUIが読み込まれた後であることだけです。 GUIがロードされている間(これは私のアプリケーションでは重い操作です)、EDTは非常にビジーであり、アニメーションを実行できないと思います。
スレッドを使用してアニメーションGIF画像を表示する方法 を参照してください。
今問題は、別のスレッド(EDTではない)でGUIをロードするのは間違っているため、問題を解決する方法がわかりません。
誰かがアイデアを持っていますか?
いくつかの重いタスクのEDTスレッドを解放し、別のスレッドでそれらを実行する必要があります。その場合、gifアニメーションは、実行中の他のプロセスと連動します。
また、別のスレッド(はい、はい、EDT内ではありません)でアプリケーションインターフェースを作成することもできますが、それを表示するまでのみです。その後、EDT内ですべての変更を行う必要があります。そうしないと、多くの問題が発生する可能性があります。
後で別のスレッドにさらに多くのUI要素を読み込むこともできます。EDT内の表示されたフレーム/コンテナーにそれらを追加することを確認してください。これが最も重要です。
以下は、「重い」インターフェイスの読み込みの小さな例です。
public static void main ( String[] args ) throws InvocationTargetException, InterruptedException
{
// Main window
final JFrame frame = new JFrame ();
final JPanel panel = new JPanel ( new FlowLayout ( FlowLayout.LEFT, 5, 5 ) )
{
public Dimension getPreferredSize ()
{
Dimension ps = super.getPreferredSize ();
ps.width = 0;
return ps;
}
};
frame.add ( new JScrollPane ( panel ) );
frame.setSize ( 600, 500 );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setLocationRelativeTo ( null );
SwingUtilities.invokeAndWait ( new Runnable ()
{
public void run ()
{
frame.setVisible ( true );
}
} );
// Load dialog
final JDialog load = new JDialog ( frame );
JPanel panel2 = new JPanel ( new BorderLayout () );
panel2.setBorder ( BorderFactory.createEmptyBorder ( 15, 15, 15, 15 ) );
load.add ( panel2 );
final JProgressBar progressBar = new JProgressBar ( 0, 100 );
panel2.add ( progressBar );
load.setModal ( false );
load.pack ();
load.setLocationRelativeTo ( frame );
SwingUtilities.invokeAndWait ( new Runnable ()
{
public void run ()
{
load.setVisible ( true );
}
} );
// Heavy task (takes approx. 10 seconds + some time on buttons creation)
for ( int i = 0; i < 100; i++ )
{
Thread.sleep ( 100 );
final JButton button = new JButton ( "Button" + i );
final int finalI = i;
// Updating panel and progress in EDT
SwingUtilities.invokeLater ( new Runnable ()
{
public void run ()
{
panel.add ( button );
button.revalidate ();
progressBar.setValue ( finalI );
}
} );
}
}
ご覧のとおり、すべてのインターフェース更新操作はEDTで行われ、その他はすべて他のスレッド内で実行されます。
また、メインスレッドはEDTスレッドではないため、すぐに重い処理を実行できます。
場合によっては、インターフェースのロードされた部分をすぐに表示する必要がないため、「重い」操作の最後にまとめて追加できます。これにより、読み込み時間が短縮され、初期化コードがはるかに簡単になります。
EDTに関する簡単な説明と答えで私が言ったこと...
...それは、Swing L&Fおよび多くのSwingベースのアプリケーションの下で3年間働いた後に私が見つけたものでした。私は多くのSwingソースを掘り下げて、広く知られていない興味深いものをたくさん見つけました。
ご存じのとおり-インターフェース更新(SwingでのEDT)の単一スレッドの全体的な考え方は、個別のコンポーネントvisual更新(およびそのイベント)を維持することですキューに入れて、そのスレッド内で1つずつ実行します。単一のフレーム内のすべてのコンポーネントがメモリに保持されているsingle画像にペイントされるため、これは主にペイントの問題を回避するために必要です。そこではペイントの順序が厳しく、あるコンポーネントが最終的なイメージで別のコンポーネントを上書きすることはありません。描画順序は、いくつかのコンポーネントまたはコンテナーを別のコンテナー内に追加することによって作成されるコンポーネントツリーに依存します(これは、Swingでアプリケーションインターフェイスを作成するときに行う基本的なことです)。
まとめると、すべてのvisual更新(それらを引き起こす可能性のあるメソッド/操作)をEDT内に保持する必要があります。その他mightはEDTの外部で行うことができます-たとえば、アプリケーションインターフェースをEDTの外部で準備できます(ここでも、コンポーネントを追加/削除/移動しない限り)すでに表示されているコンテナ)。
それでも、非常にまれなケースでは、内部の問題が発生する可能性があります。ずっと前にその質問について良い議論がありました:
http://www.velocityreviews.com/forums/t707173-why-does-jdk-1-6-recommend-creating-swing-components-on-the-edt.html =
要するに、6番目のJDKバージョン以降、Sunはドキュメントで、Swingコンポーネントの作成でさえpossible問題を回避するためにEDT内で実行する必要があると述べました。コンポーネントが作成されている間に発生するイベントが原因で、インターフェースの作成が重い特定のケースで表示される場合があります。
とにかく、ローダー/アプリケーションのスタックを回避するために、場合によっては、EDTの外部にインターフェイスを作成することがあります。他のケースでは、アプリケーションがインターフェース作成時間に行き詰まっているかどうかが問題ではない場合、EDTを使用する必要があります。そして、すべてがあなたのケースに依存するので、私はこれ以上具体的に言うことはできません...
たぶん、次のイベントやコンポーネントに干渉することなく、アプリケーションの開始直後に再生されるアニメーションを作成しようとしているのかもしれません。したがって、スプラッシュスクリーンを試してみるとよいでしょう。ここからそれについて読んでください: http://docs.Oracle.com/javase/tutorial/uiswing/misc/splashscreen.html
上記のリンクでは、Frameクラスから派生したSplashScreen
というクラスの使用法を示しています。したがって、メカニズムはそのようなものです。別のフレーム(スプラッシュスクリーン、アニメーションはここに表示されます)を表示し、しばらくしてからメインアプリケーションを起動します。
「ImageIcon」クラスを使用すると、gifアニメーションをロードできます。 「getResource()」で画像を読み込みます。これを行うために、私は通常、ファイルパスを渡すURLクラスを使用します。名前のURLが示唆するように、リモートマシンではパスは必要ありません。
URL url = this.getClass().getResource(path);
Icon myImgIcon = new ImageIcon(url);
JLabel imageLbl = new JLabel(myImgIcon);
component.add(imageLbl, BorderLayout.CENTER);
パスは、クラスフォルダー内のgifのパスになります。
参照: http://docs.Oracle.com/javase/tutorial/uiswing/components/icon.html#getresource