2010/08/06

[Java] 客制化 JProgressBar 的進度條顏色

在論壇上看到一個這樣的需求:
  • javax.swing.JProgressBar 的顯示要包含五種顏色, 第一段(0~20%)紅色, 第二段(20%~40%)橙色, 第三段(40%~60%)綠色, 第四段(60~80)黃色, 第五段(80~100)藍色.
我的想法: 寫一個繼承 javax.swing.JProgressBar 的類別, 並複寫 paint() , 來達到此目的.

作法如下:
  1. 寫一個測試用的 JFrame :
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    /**
     *
     * @author yilin
     */
    public class JProgressbarTest extends javax.swing.JFrame {
    
      private javax.swing.JButton btnRun;
      private MyProgressbar jpbTestBar;
    
      JProgressbarTest() {
        super("JProgressbarTest");
        initComponents();
        //設定JFrame的尺寸
        super.setSize(400, 100);
        //設定啟始在螢幕中央
        this.setLocationRelativeTo(null);
      }
    
      private void initComponents() {
        this.btnRun = new JButton();
        this.jpbTestBar = new MyProgressbar();
        //設定 MyProgressbar 的尺寸
        this.jpbTestBar.setPreferredSize(new Dimension(350, 20));
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    
        btnRun.setText("Run");
        //設定按鈕的 onclick 事件
        btnRun.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent evt) {
            btnRunActionPerformed(evt);
          }
        });
    
        //在 JFrame 中放入 MyProgressbar 和一個觸發事件的按鈕
        FlowLayout layout = new FlowLayout();
        getContentPane().setLayout(layout);
        getContentPane().add(this.jpbTestBar);
        getContentPane().add(this.btnRun);
        pack();
      }
    
      public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
          public void run() {
            new JProgressbarTest().setVisible(true);
          }
        });
      }
    
      //這個覆寫要存在, 不然預設會呼叫到 MyProgressbar 的 paint()
      public void paint(Graphics g) {
      }
    
      // onclick 事件
      private void btnRunActionPerformed(ActionEvent evt) {
        //設定最大值是 100
        this.jpbTestBar.setMaximum(100);
    
        //產生一個 Thread 去設定 MyProgressbar 的值
        new Thread(new Runnable() {
    
          public void run() {
            Graphics g = jpbTestBar.getGraphics();
            for (int i = 0; i <= 100; i++) {
              jpbTestBar.setValue(i);
              try {
                //產生停頓的效果
                Thread.sleep(100);
              } catch (Exception e) {
              }
            }
          }
        }).start();
      }
    }
  2. 再寫一個 class MyProgressbar  (如果要將此類別獨立成一個檔案, 要記得加入 public 的宣告):
    class MyProgressbar extends JProgressBar {
      //第一段(0~20%)紅色,第二段(20%~40%)橙色,
    //第三段(40%~60%)綠色,第四段(60~80)黃色,第五段(80~100)藍色
      float[] range = new float[]{0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
      Color[] colors = new Color[]{Color.RED, Color.ORANGE
    , Color.GREEN, Color.YELLOW, Color.BLUE};
    
      public void paint(Graphics g) {
        //如果沒有繪圖物件, 不進行後續的著色
        if (g == null) {
          return;
        }
    
        //先打底色
        g.setColor(this.getBackground());
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
    
        //計算比例
        float p = this.getValue() * 1.0f / this.getMaximum();
        //區塊的起始X座標
        int intStartX;
        //區塊的終點X座標
        int intEndX;
        for (int i = 0, j = 1; i < range.length - 1; i++, j++) {
          g.setColor(colors[i]);
          intStartX = (int) (this.getWidth() * range[i]);
          //計算終點的X座標
          intEndX = ((p >= range[i] && p < range[j])) 
    ? (int) (this.getWidth() * (p - range[i])) 
    : (int) (this.getWidth() * (range[j] - range[i]));
          g.fillRect(intStartX, 0, intEndX, this.getHeight());
    
          if (p >= range[i] && p < range[j]) {
            //若是落在區間內, 後續可不再進行 ProgressBar 的繪圖
            break;
          }
        }
      }
    }
  3. 畫面擷圖:
    1   2
  4. 修改一下, 弄個漸層的彩虹效果:
    class MyProgressbar extends JProgressBar {
      //第一段(0~20%)紅色到橙色,第二段(20%~40%)橙色到黃色
    //,第三段(40%~60%)黃色到綠色,第四段(60~80)綠色到藍色
    //,第五段(80~100)藍色到靛色
      float[] range = new float[]{0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
      Color[] colors = new Color[]{Color.RED, Color.ORANGE
    , Color.YELLOW, Color.GREEN, Color.BLUE, Color.CYAN};
    
      public void paint(Graphics g) {
        if (g == null) {
          return;
        }
        //將原本的 Graphics 轉型至 Graphics2D
        Graphics2D g2 = (Graphics2D) g;
        //先打底色
        g2.setColor(this.getBackground());
        g2.fillRect(0, 0, this.getWidth(), this.getHeight());
        //計算比例
        float p = this.getValue() * 1.0f / this.getMaximum();
        //畫區塊的起始X座標
        int intStartX;
        //畫區塊的終點X座標
        int intEndX;
        for (int i = 0, j = 1; i < range.length - 1; i++, j++) {
          intStartX = (int) (this.getWidth() * range[i]);
          //計算終點的X座標()
          intEndX = ((p >= range[i] && p < range[j])) 
    ? (int) (this.getWidth() * (p - range[i])) 
    : (int) (this.getWidth() * (range[j] - range[i]));
          //建立漸層的GradientPaint的物件
          GradientPaint gp = new GradientPaint(intStartX, 0
    , colors[i], intStartX + intEndX
    , 0, colors[j]);
          g2.setPaint(gp);
          //畫出一個矩形
          g2.fillRect(intStartX, 0, intEndX + 1, this.getHeight());
          if (p >= range[i] && p < range[j]) {
            //若是落在區間內, 後續可不再進行 ProgressBar 的繪圖
            break;
          }
        }
      }
    }
    畫面擷圖:
    3_1 4_1
如此一來就可以設計出自己想要的 ProgressBar 了.

沒有留言: