Подтвердить что ты не робот

Отображение анимации Gif в java

Здравствуйте, я пишу приложение GUI на Java 1.6 с Swing.

У меня есть всплывающий экран, который должен отображать анимацию gif, пока мой Swing gui загружается, а также немного позже.

Мой всплывающий экран - это JDialog. T анимация должна отображаться на JLabel, которая была добавлена ​​в Jdialog следующим образом:

ImageIcon myImgIcon = getMyImgIcon();
JLabel imageLbl = new JLabel(myImgIcon);
add(imageLbl, BorderLayout.CENTER); 

Теперь дело в том, что анимация отображается только после загрузки gui. Я считаю, что, когда графический интерфейс загружается (это тяжелая операция в моем приложении), EDT настолько занят, что не может запустить анимацию.

Смотрите Как показать анимированное изображение GIF с помощью потока.

Теперь мне было бы неправильно сделать загрузку графического интерфейса для другого потока (не EDT), поэтому я не знаю, как решить проблему.

Есть ли у кого-нибудь идеи?

4b9b3361

Ответ 1

Вам просто нужно освободить поток EDT от некоторых тяжелых задач и выполнить их в отдельном потоке. В этом случае gif анимация будет работать вместе с другими запущенными процессами.

Вы также можете создать свой интерфейс приложения в отдельном потоке (да, да, не внутри EDT), но только до тех пор, пока вы его не отобразите. После этого вы должны внести все изменения в EDT, иначе вы можете столкнуться с множеством проблем.

Позже вы также можете загрузить больше элементов пользовательского интерфейса в отдельном потоке, просто убедитесь, что вы добавляете их в отображаемые фреймы/контейнеры внутри 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. Я копал много исходников Swing и нашел много интересных вещей, которые не очень известны.

Как вы знаете, вся идея единого потока для обновлений интерфейса (его EDT в Swing) заключается в том, чтобы хранить отдельные визуальные обновления каждого компонента (и его события) в очереди и выполнять их один за другим внутри этого потока. Это необходимо в основном для того, чтобы избежать проблем с рисованием, поскольку каждый компонент в одном кадре окрашивается в одно изображение, которое хранится в памяти. Порядок рисования там строгий, поэтому один компонент не будет перезаписывать другой на конечном изображении. Порядок рисования зависит от дерева компонентов, которое создается путем добавления некоторых компонентов или контейнеров в другой контейнер (это основная вещь, которую вы делаете при создании любого интерфейса приложения в Swing).

Подводя итог - вы должны хранить все визуальные обновления (методы/операции, которые могут их вызвать) внутри EDT. Все остальное может быть сделано за пределами 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 должно выполняться внутри EDT, чтобы избежать возможных проблем. Они могут появляться в некоторых особых случаях при создании сложных интерфейсов из-за событий, которые происходят во время создания компонентов.

В любом случае, я бы сказал, что в некоторых случаях вы можете создать свой интерфейс вне EDT, чтобы избежать зависания загрузчика/приложения. В других случаях, когда не имеет значения, зависло ли приложение на время создания интерфейса, следует использовать EDT. И я не могу сказать ничего более конкретного, так как все зависит от вашего случая...

Ответ 2

Возможно, вы пытаетесь создать анимацию, которая должна воспроизводиться только в начале вашего приложения, не мешая предстоящим событиям или компонентам. Поэтому вы можете попробовать создать заставки. Читайте об этом отсюда: http://docs.oracle.com/javase/tutorial/uiswing/misc/splashscreen.html

В приведенной выше ссылке демонстрируется использование класса с именем SplashScreen, который только что получен из класса Frame. Таким образом, механизм выглядит следующим образом: вы показываете отдельный кадр (заставка, анимация) и через некоторое время запускается основное приложение.

Ответ 3

'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

Ответ 4

Внутри вашего JFrame или чего-то еще, используйте это:

Icon imgIcon = new ImageIcon(this.getClass().getResource("ajax-loader.gif"));
JLabel label = new JLabel(imgIcon);
label.setBounds(669, 42, 45, 15); // You can use your own values
frame.getContentPane().add(label);

Источник: Как отобразить анимированный GIF в Java Swing