2010/08/06

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

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

作法如下:
  1. 寫一個測試用的 JFrame :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    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 的宣告):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    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. 修改一下, 弄個漸層的彩虹效果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    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 了.

沒有留言: