JProgressbar:如何根据进度更改颜色?

4

根据进度值更改进度条颜色是否可能?我尝试了以下内容但不起作用:

percentUsed = (int)(((float) used / (float) max) * BAR_PERCENTAGE);
      if (percentUsed >= ORANGE_THRESHOLD &&  percentUsed < RED_THRESHOLD) {
        if (!m_orangeIndicator) {
          LOG.warn(String.format("Memory usage exceeds %d percent.", ORANGE_THRESHOLD));
          m_orangeIndicator = true;
        }
        colour = Color.ORANGE;
        m_redIndicator = false;
      }
      else if (percentUsed >= RED_THRESHOLD) {
        if (!m_redIndicator) {
          LOG.warn(String.format("Memory usage exceeds %d percent.", RED_THRESHOLD));
          m_orangeIndicator = true;
          m_redIndicator = true;
        }
        colour = Color.RED;
      }
      else {
        m_orangeIndicator = false;
        m_redIndicator = false;
        colour = Color.GREEN;
      }
      m_memUsageBar.setForeground(colour);
      m_memUsageBar.setValue(percentUsed);
      m_memUsageBar.updateUI();

我猜这不是一件简单的事情,因为JProgressbar并不是用来这样使用的... 但是是否有可能或者是否有替代方案?

1
首先要注意的是 - 不要调用 updateUI。这与请求重绘没有任何直接关系。应该使用 invalidaterepaint - MadProgrammer
是的,“updateUI”部分是我试图抓住救命稻草。我尝试了invalidate和repaint,但没有看到任何变化。 - Foo
看起来 Windows L&F 忽略了背景和前景设置,这真的很烦人。 - MadProgrammer
4个回答

8

图片描述在此处输入

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import javax.swing.event.*;

public class GradientPalletProgressBarDemo {
  public JComponent makeUI() {
    final JProgressBar progressBar = new JProgressBar();
    progressBar.setOpaque(false);
    progressBar.setUI(new GradientPalletProgressBarUI());

    JPanel p = new JPanel();
    p.add(progressBar);
    p.add(new JButton(new AbstractAction("Start") {
      @Override public void actionPerformed(ActionEvent e) {
        SwingWorker<Void,Void> worker = new SwingWorker<Void,Void>() {
          @Override public Void doInBackground() {
            int current = 0, lengthOfTask = 100;
            while(current<=lengthOfTask && !isCancelled()) {
              try { // dummy task
                Thread.sleep(50);
              } catch(InterruptedException ie) {
                return null;
              }
              setProgress(100 * current / lengthOfTask);
              current++;
            }
            return null;
          }
        };
        worker.addPropertyChangeListener(new ProgressListener(progressBar));
        worker.execute();
      }
    }));
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new GradientPalletProgressBarDemo().makeUI());
    frame.setSize(320, 240);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}
class ProgressListener implements PropertyChangeListener {
  private final JProgressBar progressBar;
  ProgressListener(JProgressBar progressBar) {
    this.progressBar = progressBar;
    this.progressBar.setValue(0);
  }
  @Override public void propertyChange(PropertyChangeEvent evt) {
    String strPropertyName = evt.getPropertyName();
    if("progress".equals(strPropertyName)) {
      progressBar.setIndeterminate(false);
      int progress = (Integer)evt.getNewValue();
      progressBar.setValue(progress);
    }
  }
}
class GradientPalletProgressBarUI extends BasicProgressBarUI {
  private final int[] pallet;
  public GradientPalletProgressBarUI() {
    super();
    this.pallet = makeGradientPallet();
  }
  private static int[] makeGradientPallet() {
    BufferedImage image = new BufferedImage(100, 1, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2  = image.createGraphics();
    Point2D start  = new Point2D.Float(0f, 0f);
    Point2D end    = new Point2D.Float(99f, 0f);
    float[] dist   = {0.0f, 0.5f, 1.0f};
    Color[] colors = { Color.RED, Color.YELLOW, Color.GREEN };
    g2.setPaint(new LinearGradientPaint(start, end, dist, colors));
    g2.fillRect(0, 0, 100, 1);
    g2.dispose();

    int width  = image.getWidth(null);
    int[] pallet = new int[width];
    PixelGrabber pg = new PixelGrabber(image, 0, 0, width, 1, pallet, 0, width);
    try {
      pg.grabPixels();
    } catch(Exception e) {
      e.printStackTrace();
    }
    return pallet;
  }
  private static Color getColorFromPallet(int[] pallet, float x) {
    if(x < 0.0 || x > 1.0) {
      throw new IllegalArgumentException("Parameter outside of expected range");
    }
    int i = (int)(pallet.length * x);
    int max = pallet.length-1;
    int index = i<0?0:i>max?max:i;
    int pix = pallet[index] & 0x00ffffff | (0x64 << 24);
    return new Color(pix, true);
  }
  @Override public void paintDeterminate(Graphics g, JComponent c) {
    if (!(g instanceof Graphics2D)) {
      return;
    }
    Insets b = progressBar.getInsets(); // area for border
    int barRectWidth  = progressBar.getWidth()  - (b.right + b.left);
    int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);
    if (barRectWidth <= 0 || barRectHeight <= 0) {
      return;
    }
    int cellLength = getCellLength();
    int cellSpacing = getCellSpacing();
    // amount of progress to draw
    int amountFull = getAmountFull(b, barRectWidth, barRectHeight);

    if(progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
      // draw the cells
      float x = amountFull / (float)barRectWidth;
      g.setColor(getColorFromPallet(pallet, x));
      g.fillRect(b.left, b.top, amountFull, barRectHeight);

    } else { // VERTICAL
      //...
    }
    // Deal with possible text painting
    if(progressBar.isStringPainted()) {
      paintString(g, b.left, b.top, barRectWidth, barRectHeight, amountFull, b);
    }
  }
}

感谢 PixelGrabber 的课程,今天的学习内容非常有收获 :-) - mKorbel
@mKorbel 谢谢,我已经删除了未使用的变量。 - aterai

4
您可以采取以下任一方式:
           UIManager.put("ProgressBar.background", Color.BLACK); //colour of the background    

UIManager.put("ProgressBar.foreground", Color.RED);  //colour of progress bar
    UIManager.put("ProgressBar.selectionBackground",Color.YELLOW);  //colour of percentage counter on black background    
UIManager.put("ProgressBar.selectionForeground",Color.BLUE);  //colour of precentage counter on red background

这里有一个帖子。 设置JProgressBar文本颜色

这个链接也提供了一个演示样例。 如何更改进度条的颜色


这是实现动态更改进度条颜色的方法吗?我认为这更多是将进度条初始化为特定颜色的过程? - Foo
我尝试将这4行代码放在JProgressbar初始化之前,但我没有注意到颜色的变化... - Foo
应用程序运行的操作系统是否重要? - Foo
这可能很重要,因为苹果的默认外观与Windows不同。 - UVM

1

正如您所注意到的,不支持基于值的“进度”绘制。理论上,动态设置progressBar实例的前景(即根据值)是正确的方法。

但是:progressBar绘制的细节高度依赖于LAF(外观和感觉)。

  • 在简单的LAF(如Metal)中,设置前景很好用。
  • 在完全可定制的LAF(如基于合成的LAF)中,支持每个实例的皮肤(例如Nimbus),您可以尝试提供使用前景的Painter。
  • 在不可定制的LAF中,除了覆盖具体的LAF并尝试钩入绘图代码之外,没有其他方法。

一些片段:

    final JProgressBar bar = new JProgressBar();
    // used for Nimbus (beware: just a proof-of-concept - this looks extremely ugly!)
    Painter p = new Painter() {

        @Override
        public void paint(Graphics2D g, Object object, int width, int height) {
            JProgressBar bar = (JProgressBar) object;
            g.setColor(bar.getForeground());
            g.fillRect(0, 0, width, height);
        }

    };
    // install custom painter on the bar
    UIDefaults properties = new UIDefaults();
    properties.put("ProgressBar[Enabled].foregroundPainter", p);
    bar.putClientProperty("Nimbus.Overrides", properties);

    // simulate progress
    Action action = new AbstractAction("timer") {

        @Override
        public void actionPerformed(ActionEvent e) {
            bar.setValue(bar.getValue() + 1);
            // change foreground value-dependent
            if (bar.getValue() > 10) {
                bar.setForeground(Color.RED);
            }
        }
    };
    Timer timer = new Timer(100, action);
    timer.start();

0

感谢您的帮助,但是通过试错,我找到了一种方法来使用我的现有代码来改变颜色。

我不确定这是否是JPrgressbar的特殊之处,但如果调用以下内容,则可以最小限度地重新编写动态更改前景进度条颜色:

progressBar.setStringPainted(true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接