JTabbedPane: показать ход выполнения задачи на вкладке

У меня есть простое Java-приложение Swing, которое выполняет поиск, и результаты отображаются на новой вкладке. Пока идет поиск, я хочу показать значок прогресса или анимацию в заголовке вкладки. Я попытался добавить значок gif, но он не анимируется. Есть ли причина, по которой это не работает?


person Paco    schedule 07.04.2012    source источник
comment
«Путь наименьшего удивления» для этого будет либо JProgressBar среди компонентов интересующей вкладки, либо общим индикатором выполнения (за пределами области с вкладками), который показывает ход выполнения любой выбранной вкладки. Есть предложения? tnx 1) Не делайте этого. 2) Правильно произносите такие слова, как «спасибо», или, что еще лучше, пропускайте их — это шум.   -  person Andrew Thompson    schedule 07.04.2012
comment
Спасибо, но я хочу реализовать что-то вроде вкладок firefox (со значком, показывающим ход загрузки).   -  person Paco    schedule 07.04.2012
comment
Ну, фу, но «у нас есть технология», так что смотрите мой ответ. Читателю остается в качестве упражнения выбрать цвета (стиль изображения и т. д.), которые не выглядят так, как будто их придумал сумасшедший шимпанзе.   -  person Andrew Thompson    schedule 07.04.2012


Ответы (3)


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

И, как всегда, этот сайт наполнен примерами. Например, мой предыдущий ответ использует класс SwingWorker, чтобы показать прогресс до Пользователь

Изменить

Поскольку я пропустил часть названия вкладки в вашем вопросе. Вы можете создать «значок прогресса» и установить его на вкладке. Затем SwingWorker можно использовать для обновления значка.

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

Изменить2

Поскольку кажется, что мой Mac в сочетании с JDK1.7 значительно упрощает отображение анимированного gif, чем на других системах, я также создал небольшой SSCCE, очень похожий на тот, что у Эндрю, но с вращающимся значком, который не похож на него. был создан, цитирую, «сумасшедшим шимпанзе». Код вращающегося значка взят с этого сайта (я использовал урезанный вниз и добавил таймер). Единственное, чем я не очень доволен, это тот факт, что мне нужно передать панель с вкладками вращающемуся значку для запуска. Возможное решение — вытащить таймер за пределы класса RotatingIcon, но это всего лишь SSCCE. Изображения не включены, но были найдены с помощью Google.

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTabbedPane;
import javax.swing.Timer;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;

public class ProgressTabbedPane {

  public static void main( String[] args ) {
    EventQueue.invokeLater( new Runnable() {
      @Override
      public void run() {
        JFrame frame = new JFrame( "RotatingIcon" );
        JTabbedPane tabbedPane = new JTabbedPane(  );
        tabbedPane.addTab( "Searching", new RotatingIcon( new ImageIcon( "resources/images/progress-indeterminate.png" ), tabbedPane ),
                           new JLabel( new ImageIcon( "resources/images/rotatingIcon.gif" ) ) );
        frame.getContentPane().add( tabbedPane );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.pack();
        frame.setVisible( true );
      }
    } );
  }

  private static class RotatingIcon implements Icon{
    private final Icon delegateIcon;
    private double angleInDegrees = 90;
    private final Timer rotatingTimer;
    private RotatingIcon( Icon icon, final JComponent component ) {
      delegateIcon = icon;
      rotatingTimer = new Timer( 100, new ActionListener() {
        @Override
        public void actionPerformed( ActionEvent e ) {
          angleInDegrees = angleInDegrees + 10;
          if ( angleInDegrees == 360 ){
            angleInDegrees = 0;
          }
          component.repaint();
        }
      } );
      rotatingTimer.setRepeats( false );
      rotatingTimer.start();
    }

    @Override
    public void paintIcon( Component c, Graphics g, int x, int y ) {
      rotatingTimer.stop();
      Graphics2D g2 = (Graphics2D )g.create();
      int cWidth = delegateIcon.getIconWidth() / 2;
      int cHeight = delegateIcon.getIconHeight() / 2;
      Rectangle r = new Rectangle(x, y, delegateIcon.getIconWidth(), delegateIcon.getIconHeight());
      g2.setClip(r);
      AffineTransform original = g2.getTransform();
      AffineTransform at = new AffineTransform();
      at.concatenate(original);
      at.rotate(Math.toRadians( angleInDegrees ), x + cWidth, y + cHeight);
      g2.setTransform(at);
      delegateIcon.paintIcon(c, g2, x, y);
      g2.setTransform(original);
      rotatingTimer.start();
    }

    @Override
    public int getIconWidth() {
      return delegateIcon.getIconWidth();
    }

    @Override
    public int getIconHeight() {
      return delegateIcon.getIconHeight();
    }
  } 
}

Скриншот для справки. Жаль, что значки на скриншоте не вращаются. Снимок экрана SSCCE

person Robin    schedule 07.04.2012
comment
Это все хорошо, но ОП хочет отображать ход выполнения на самой вкладке. Я не вижу вашего ответа, касающегося этой части. - person Hovercraft Full Of Eels; 07.04.2012
comment
О, я не видел части название вкладки, только то, что он использовал панель с вкладками. Тогда действительно это не ответ. Посмотрим, смогу ли я добавить что-то полезное к этому ответу или удалить его в противном случае. - person Robin; 07.04.2012
comment
Спасибо за ответ, но в моем случае я не могу использовать этот метод: программа не может получить доступ к прогрессу поиска (выполняется сервером). Поэтому я должен показывать значок прогресса и скрывать его только тогда, когда я получаю ответ от сервера. - person Paco; 07.04.2012
comment
@Robin Было бы здорово, если бы J2SE поддерживал анимированные GIF-файлы «из коробки» в большем количестве ситуаций. Я попробовал этот анимированный GIF (красивое изображение, кстати) в качестве значка вкладки, и нет, он остается статичным. - person Andrew Thompson; 07.04.2012
comment
@ Эндрю Томпсон Согласен. Но воссоздать анимацию с этим значком так же просто, как использовать таймер Swing и поворачивать значок на каждом этапе (см. эта ссылка для поворота значка) - person Robin; 07.04.2012
comment
@Robin Смотрите мой ответ, чтобы узнать об анимации. ;) - person Andrew Thompson; 07.04.2012
comment
@AndrewThompson на моем Mac (JDK1.7) изображение красиво вращается без каких-либо дополнительных шагов. Как на вкладке, так и в JLabel, как контент. Попробую, что мне делать со статической иконкой - person Robin; 07.04.2012
comment
@ Робин, я добавлю это в свой список «странных маленьких ошибок x-plat». Другой вопрос, который мне попался, касался звука Java Clip. В то время я разрабатывал Ubuntu, и реализация JRE позволяла использовать клипы произвольного размера (по крайней мере, до OutOfMemoryError). Но в Sun JRE для Windows 1 секунда звука CD-качества была всем, что можно было загрузить. :( Кстати - только что проверил эту анимацию изображения на Win 7 с использованием Java 1.6.0_29. - person Andrew Thompson; 07.04.2012
comment
@AndrewThompson Я добавил немного кода. JLabel использует значок, который я разместил здесь, и он просто вращается сам по себе. PNG является статическим и использует таймер для вращения. +1 за ваш комментарий про шимпанзе, кстати - person Robin; 07.04.2012
comment
Ну фигня. Жаль, что я не проголосовал раньше. Тогда я мог бы сделать это сейчас. ;) Хороший пример, и, как всегда с вами, Mac. пользователи - отличный снимок экрана. Вероятно, это происходит автоматически, но мне нравится, как снимается тень окна. :) - person Andrew Thompson; 07.04.2012
comment
@AndrewThompson Происходит действительно автоматически, не могу взять на себя ответственность за это. И, пожалуй, самое время прекратить комментировать, я получаю предложение перенести все это в чат. - person Robin; 07.04.2012

Анимированное изображение в виде значка вкладки

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

public class ImageOnTab {

    ImageOnTab() {

        final BufferedImage image = new BufferedImage(
            32,32,BufferedImage.TYPE_INT_RGB);
        final JTabbedPane pane = new JTabbedPane();
        ImageIcon icon = new ImageIcon(image);
        pane.addTab( "Progress", icon, new JTree() );

        ActionListener listener = new ActionListener() {

            int x = 0;
            int step = 1;

            public void actionPerformed(ActionEvent ae) {
                Graphics g = image.createGraphics();
                x+=step;
                if (step>0) {
                    if (x>32) {
                        step=-step;
                    }
                } else if (x<0) {
                    step=-step;
                }

                g.setColor(Color.ORANGE);
                g.fillRect(0,0,32,32);

                g.setColor(Color.RED);
                g.fillRect(0,0,x,32);

                g.dispose();

                pane.repaint();
            }
        };

        Timer timer = new Timer(100,listener);
        timer.start();

        JOptionPane.showMessageDialog(null, pane);
    }

    public static void main(String[] args) throws Exception {
        //Create the GUI on the event dispatching thread
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                new ImageOnTab();
            }
        });
    }
}
person Andrew Thompson    schedule 07.04.2012
comment
Отлично! См. также этот связанный пример. - person trashgod; 07.04.2012
comment
@trashgod Джиш.. Я должен был знать а) у тебя уже был пример для этого б) с одним из тех прекрасных Mac. скриншоты. c) за то, что я уже проголосовал, и d) это было лучше, чем все, что я мог взломать за несколько «сумасшедших» моментов! (За исключением того, что мне все еще нравится тот факт, что мой подход не должен ничего отменять. Не уверен, является ли это «хорошим дизайном» в данном случае или просто «поркой дохлой лошади».) - person Andrew Thompson; 07.04.2012
comment
Я считаю, что они дополняют друг друга во всех отношениях; цитируется. - person trashgod; 07.04.2012

@Эндрю Томпсон

It would be great if the J2SE supported animated GIFs 'out of the box' 
in more situations. I tried that animated GIF (nice image, BTW) as a 
tab icon, and no, it remains static. 

Я не хочу читать целиком ...., но соберу код вашим и @trashgod's majesty

1) используйте Htlm (я плохо разбираюсь в простом Html)

2) используйте GlassPane с JLabel#(setOpaque(true))

3) используйте JLayer (JXLayer лучше, потому что Sn'Oracle удаляет важные методы == мое мнение)

4) вам нужно форсировать ..... для Swing JComponents от @aterai

5) Анимированная иконка Роба несколько раз упомянула о поддержке со стороны Роба для JTabbedPane

код

import java.awt.*;
import java.awt.event.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
//https://stackoverflow.com/questions/3483485/java-jprogressbar-or-equivalent-in-a-jtabbedpane-tab-title/3484251#3484251
public class JTabbedTest {

    private JFrame f = new JFrame();
    private JTabbedPane jtp = new JTabbedPane();
    private URL url = null;

    public JTabbedTest() {
        try {
            url = new URL("http://pscode.org/media/starzoom-thumb.gif");
        } catch (MalformedURLException ex) {
            Logger.getLogger(JTabbedTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        ImageIcon ii = new ImageIcon(url);

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jtp.setPreferredSize(new Dimension(400, 200));
        createTab("Reds", Color.RED);
        createTab("Greens", Color.GREEN);
        createTab("Blues", Color.BLUE);
        f.add(jtp, BorderLayout.CENTER);

        jtp.setTitleAt(2, "<html><img src=" + ii + " width=20 height=20></img></html>");

        // change foreground Color for disabled tab        
        /*jtp.setTitleAt(2, "<html><font color=" + (jtp.isEnabledAt(2) ? "black" : "red") + ">"
                + jtp.getTitleAt(2) + "</font></html>");*/

        Rectangle tabBounds = jtp.getBoundsAt(0);
        Container glassPane = (Container) f.getRootPane().getGlassPane();
        glassPane.setVisible(true);
        glassPane.setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.NONE;
        gbc.insets = new Insets(tabBounds.y + 23, 0, 0, 5);
        gbc.anchor = GridBagConstraints.NORTHEAST;
        JButton button = new JButton("My Button Position", ii);
        button.setPreferredSize(new Dimension(button.getPreferredSize().width, (int) tabBounds.getHeight() - 2));
        glassPane.add(button, gbc);
        f.pack();
        f.setVisible(true);
    }

    private void createTab(String name, Color color) {
        ProgressIcon icon = new ProgressIcon(color);
        jtp.addTab(name, icon, new ColorPanel(jtp, icon));
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JTabbedTest jTabbedTest = new JTabbedTest();
            }
        });
    }

    private static class ColorPanel extends JPanel implements ActionListener {

        private static final Random rnd = new Random();
        private static final long serialVersionUID = 1L;
        private final Timer timer = new Timer(1000, this);
        private final JLabel label = new JLabel("Stackoverflow!");
        private final JTabbedPane parent;
        private final ProgressIcon icon;
        private final int mask;
        private int count;

        public ColorPanel(JTabbedPane parent, ProgressIcon icon) {
            super(true);
            this.parent = parent;
            this.icon = icon;
            this.mask = icon.color.getRGB();
            this.setBackground(icon.color);
            label.setForeground(icon.color);
            this.add(label);
            timer.start();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            this.setBackground(new Color(rnd.nextInt() & mask));
            this.icon.update(count += rnd.nextInt(8));
            this.parent.repaint();
        }
    }

    private static class ProgressIcon implements Icon {

        private static final int H = 16;
        private static final int W = 3 * H;
        private Color color;
        private int w;

        public ProgressIcon(Color color) {
            this.color = color;
        }

        public void update(int i) {
            w = i % W;
        }

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            g.setColor(color);
            g.fillRect(x, y, w, H);
        }

        @Override
        public int getIconWidth() {
            return W;
        }

        @Override
        public int getIconHeight() {
            return H;
        }
    }
}
person mKorbel    schedule 07.04.2012
comment
Спасибо за повторную установку этого ответа. +1 :) - person Andrew Thompson; 09.04.2012