web-dev-qa-db-ja.com

javaでのGifアニメーションの表示

こんにちは、私は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をロードするのは間違っているため、問題を解決する方法がわかりません。

誰かがアイデアを持っていますか?

14
whomaniac

いくつかの重いタスクの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を使用する必要があります。そして、すべてがあなたのケースに依存するので、私はこれ以上具体的に言うことはできません...

7
Mikle Garin

たぶん、次のイベントやコンポーネントに干渉することなく、アプリケーションの開始直後に再生されるアニメーションを作成しようとしているのかもしれません。したがって、スプラッシュスクリーンを試してみるとよいでしょう。ここからそれについて読んでください: http://docs.Oracle.com/javase/tutorial/uiswing/misc/splashscreen.html

上記のリンクでは、Frameクラスから派生したSplashScreenというクラスの使用法を示しています。したがって、メカニズムはそのようなものです。別のフレーム(スプラッシュスクリーン、アニメーションはここに表示されます)を表示し、しばらくしてからメインアプリケーションを起動します。

3
Juvanis

「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

1
Tony