如何在不使用JProgressBar的情况下制作进度条?

5
我正在考虑在我制作的游戏中加入一个进度条,倒计时从5到20之间的随机数开始,直到0。这是我的代码:
``` package Software; ```
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;

public class CountDownProgressBar extends JPanel {

    RandomValues randomNum;
    int timeValue;
    Timer timer;
    int length, newLength;

    public CountDownProgressBar() {
        timer = new Timer(24, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
               for (int i = timeValue; i > 0; --i) {
                        newLength = length - (length / timeValue);
                        length = newLength;
                        repaint();
                    }
                }
            });
        randomNum = new RandomValues();
        this.timeValue = 0;
        this.setBackground(Color.GRAY);
    }

    @Override
    public void paint(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.green);
        g.fillRect(0, 0, length, 50);
    }

    public void setTimeLength() {
        this.timeValue = randomNum.integer(5, 20);
    }

    public int getTimeLength() {
        return this.timeValue;
    }
}

我知道大部分内容都是错误的,我只是想不出如何解决。它不能使用JProgressBar,而是要使用图形(一个绿色的矩形),在时间倒计时时越来越小。我该如何使屏幕可以调整大小,而进度条仍然相对于屏幕大小进行倒计时。这样它就不是固定大小,而是横跨屏幕底部。

你可以制作一个自定义的JComponent。 - Extreme Coders
6
1)可以直接将JProgressBar绘制到Graphics实例上。 2)不要阻塞EDT(事件派发线程),否则GUI会“冻结”。 请使用Swing Timer来执行重复任务,而不是调用Thread.sleep(n)。有关更多详细信息,请参见Swing中的并发性。 3)“沿屏幕底部移动。” 将JProgressBar放在BorderLayoutPAGE_END(即底部)中,而不是覆盖游戏播放区域。 - Andrew Thompson
+1 给 @AndrewThompson 的评论。请查看我的答案,其中使用了该逻辑:https://dev59.com/zmzXa4cB1Zd3GeqPUID6#14052118 - David Kroukamp
1
@andrew 这是一个答案,而不是评论。为什么不将其发布为答案呢? - dstibbe
1
@dstibbe 这种类型的评论/回答通常是一个判断调用。由于现在在线程中有3个好的例子,或者链接到它们,我想这次我会把它留作评论。 - Andrew Thompson
显示剩余2条评论
4个回答

11

这是我很久以前编写的自定义 JProgressBar 代码(虽然问题中说不要使用 JProgressBar,但您可以从中获取代码想法),它的设计目的是模仿 Ubuntu 9.04 中“Human”主题的进度条外观:

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JProgressBar;

/**
 * A progressBar that looks like the progress bar from Ubuntu 9.04 Human Theme
 */
public class HumanProgressBar extends JProgressBar {
    private static final long serialVersionUID = 1L;

    private static final String DISABLED_PERCENT_STRING = " --- ";

    private static final Color gradientEndingColor = new Color(0xc0c0c0);
    private static final Color borderColor = new Color(0x736a60);
    private static final Color disabledBorderColor = new Color(0xbebebe);    

    private static final Composite transparent = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.45f);
    private static final Composite veryTransparent = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25f);

    private static GradientPaint gradient;

    private int oldWidth;
    private int oldHeight;

    private int displayWidth;
    private int displayHeight;

    private int insets[] = new int[4];
    private static final int TOP_INSET = 0;
    private static final int LEFT_INSET = 1;
    private static final int BOTTOM_INSET = 2;
    private static final int RIGHT_INSET = 3;

    private static final int PREFERRED_PERCENT_STRING_MARGIN_WIDTH = 3;

    public static final Color PREFERRED_PROGRESS_COLOR = new Color(0x1869A6);

    private boolean percentStringVisible = true;

    private Color progressColor;

    private String maxPercentString;

    public HumanProgressBar() {
        progressColor = PREFERRED_PROGRESS_COLOR;
    }

    public void updateGraphics() {
        update(getGraphics());
    }

    @Override
    protected void paintComponent(Graphics g) {
        int w = displayWidth != 0 ? displayWidth - 1 : getWidth() - 1;
        int h = displayHeight != 0 ? displayHeight - 1 : getHeight() - 1;

        int x = insets[LEFT_INSET];
        int y = insets[TOP_INSET];
        w -= (insets[RIGHT_INSET] << 1);
        h -= (insets[BOTTOM_INSET] << 1);

        if (gradient == null) {
            gradient = new GradientPaint(0.0f, 0.0f, Color.WHITE, 0.0f, h, gradientEndingColor);
        }
        Graphics2D g2d = (Graphics2D) g;
        // Clean background
        if (isOpaque()) {
            g2d.setColor(getBackground());
            g2d.fillRect(0, 0, getWidth(), getHeight());
        }

        g2d.translate(x, y);

        if (percentStringVisible) {
            FontMetrics fm = g.getFontMetrics();
            int stringW = 0;
            int stringH = 0;

            g2d.setColor(getForeground());

            if (isEnabled()) { 
                int p = getValue();
                String percent = Integer.toString(p, 10) + "%";
                if (p < 10) {
                    percent = "0" + percent;
                }

                if (maxPercentString == null) {
                    maxPercentString = Integer.toString(getMaximum(), 10) + "%";
                }
                stringW = fm.stringWidth(maxPercentString);
                stringH = ((h - fm.getHeight()) / 2) + fm.getAscent();

                g2d.drawString(percent, w - stringW, stringH);
            } else {
                stringW = fm.stringWidth(DISABLED_PERCENT_STRING);
                stringH = ((h - fm.getHeight()) / 2) + fm.getAscent();

                g2d.drawString(DISABLED_PERCENT_STRING, w - stringW, stringH);
            }
            w -= (stringW + PREFERRED_PERCENT_STRING_MARGIN_WIDTH);            
        }

        // Control Border
        g2d.setColor(isEnabled() ? borderColor : disabledBorderColor);
        g2d.drawLine(1, 0, w - 1, 0);
        g2d.drawLine(1, h, w - 1, h);
        g2d.drawLine(0, 1, 0, h - 1);
        g2d.drawLine(w, 1, w, h - 1);

        // Fill in the progress
        int min = getMinimum();
        int max = getMaximum();
        int total = max - min;
        float dx = (float) (w - 2) / (float) total;
        int value = getValue();
        int progress = 0; 
        if (value == max) {
            progress = w - 1;
        } else {
            progress = (int) (dx * getValue());            
        }

        g2d.setColor(progressColor);
        g2d.fillRect(1, 1, progress, h - 1);

        // A gradient over the progress fill
        g2d.setPaint(gradient);
        g2d.setComposite(transparent);
        g2d.fillRect(1, 1, w - 1, (h >> 1));
        final float FACTOR = 0.20f;
        g2d.fillRect(1, h - (int) (h * FACTOR), w - 1, (int) (h * FACTOR));

        if (isEnabled()) {
            for (int i = h; i < w; i += h) {
                g2d.setComposite(veryTransparent);
                g2d.setColor(Color.GRAY);
                g2d.drawLine(i, 1, i, h - 1);
                g2d.setColor(Color.WHITE);
                g2d.drawLine(i + 1, 1, i + 1, h - 1);
            }
        } else {
            for (int i = 0; i < w; i += h) {
                g2d.setComposite(veryTransparent);
                g2d.setColor(Color.RED);
                g2d.drawLine(i, h - 1, i + h, 1);
                g2d.setColor(Color.WHITE);
                g2d.drawLine(i + 1, h - 1, i + 1 + h, 1);
            }            
        }
    }

    public void setInsets(int top, int left, int bottom, int right) {
        insets[TOP_INSET] = top;
        insets[LEFT_INSET] = left;
        insets[BOTTOM_INSET] = bottom;
        insets[RIGHT_INSET] = right;
    }

    public void setPercentStringVisible(boolean percentStringVisible) {
        this.percentStringVisible = percentStringVisible;
    }

    @Override
    protected void paintBorder(Graphics g) {
    }

    @Override
    public void validate() {
        int w = getWidth();
        int h = getHeight();

        super.validate();
        if (oldWidth != w || oldHeight != h) {
            oldWidth = w;
            oldHeight = h;
            gradient = null;
        }
    }

    @Override
    public void setMaximum(int n) {
        super.setMaximum(n);
        maxPercentString = Integer.toString(n, 10) + "%";
    }

    public void setDisplaySize(int width, int height) {
        displayWidth = width;
        displayHeight = height;
    }

    public Color getProgressColor() {
        return progressColor;
    }

    public void setProgressColor(Color progressColor) {
        this.progressColor = progressColor;
    }
}

这是一个组件的测试程序示例:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class T extends JFrame {
    public T() {
        super();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(null);
        this.setSize(350, 75);
        HumanProgressBar p = new HumanProgressBar();
        p.setValue(50);
        p.setBounds(15, 15, 300, 15);
        this.add(p);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new T();
            }
        });
    }
}

enter image description here


1
+1个好例子。请使用SwingUtilities.invokeLater来启动Swing界面。 - vels4j
谢谢,这有些帮助。只需要一个非常简单的倒计时,从20开始到0,填满屏幕底部的整个JPanel。每秒钟,会取走栏的一部分,直到时间用完。 - KrazyKat89
非常好,如果楼主没有说不使用JProgressBar类的话。 - David Kroukamp

3
您可能可以适应ProgressIcon,在JTabbedPane的选项卡中如此处所示。

image


2

对AndrewThompsons评论点个赞。

这里有一个我制作的简短示例,不是最好的,但显示了创建自己的JProgressBar所需的逻辑,使用JPanel

enter image description here

它也可以设置为不确定:

enter image description here

通过取消注释:

    myProgressBar.setBackground(new Color(0, 255, 0, 127));
    myProgressBar.setIndeterminate(true); //if progress unknown show ball moving from side to side

CustomProgressBarTest.java:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.LineBorder;

public class CustomProgressBarTest {

    public CustomProgressBarTest() {
        createAndShowGui();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new CustomProgressBarTest();
            }
        });
    }

    private void createAndShowGui() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final MyProgressBar myProgressBar = new MyProgressBar(0, 100);
        myProgressBar.setProgressColor(new Color(0, 255, 0, 127));
        //myProgressBar.setIndeterminate(true); //if progress unknown show ball moving from side to side

        JPanel dummyPanel = new JPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(300, 300);
            }
        };

        dummyPanel.add(new JLabel("DummyPanel"));

        frame.add(dummyPanel);

        frame.add(myProgressBar, BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);

        //create timer to decrease progressbar
        createAndStartDecrementTimer(myProgressBar);

        //create timer to increase progressbar
        //myProgressBar.setValue(0);//set to 0 so we can increment
        //createAndStartIncrementTimer(myProgressBar);
    }

    private void createAndStartIncrementTimer(final MyProgressBar myProgressBar) {
        Timer progressBarIncrementTimer = new Timer(100, new AbstractAction() {
            int count = 0;

            @Override
            public void actionPerformed(ActionEvent ae) {
                if (count == 100) {
                    System.out.println("Done");
                    ((Timer) ae.getSource()).stop();
                } else if (count < 100) {
                    //System.out.println("Here");
                    count++;
                    myProgressBar.setValue(count);
                }
            }
        });
        progressBarIncrementTimer.start();
    }

    private void createAndStartDecrementTimer(final MyProgressBar myProgressBar) {
        Timer progressBArCountDownTimer = new Timer(100, new AbstractAction() {
            int count = 100;

            @Override
            public void actionPerformed(ActionEvent ae) {
                if (count == 0) {
                    System.out.println("Done");
                    ((Timer) ae.getSource()).stop();
                } else if (count > 0) {
                    count--;
                    myProgressBar.setValue(count);
                    System.out.println(myProgressBar.getValue());
                }
            }
        });
        progressBArCountDownTimer.start();
    }
}

class MyProgressBar extends JPanel {

    private final int minValue, maxValue;
    private boolean indeterminate = false;
    private int currentValue;
    private ArrayList<Rectangle> rects = new ArrayList<>();
    private Color PROGRESS_COLOR = Color.blue;
    private int removeValue = 0;
    private Timer indeterminateTimer;
    private int x = 0, y = 0, ballSize = 25;
    private boolean changeDirection = false;

    public MyProgressBar(int min, int max) {
        indeterminateTimer = new Timer(50, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                repaint();
            }
        });
        maxValue = max;
        minValue = min;
        currentValue = maxValue;
        setBorder(new LineBorder(Color.BLACK));
    }

    @Override
    protected void paintComponent(Graphics grphcs) {
        super.paintComponent(grphcs);
        Graphics2D g2d = (Graphics2D) grphcs;

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        if (!indeterminate) {//if normal progress bar
            rects.clear();
            int rectWidths = getWidth() / maxValue;
            int startingX = 0;

            if (currentValue < maxValue) {//we started off with a value less than the max
                for (int i = minValue; i < currentValue; i++) {
                    rects.add(new Rectangle(startingX, 0, rectWidths, getHeight()));
                    startingX += rectWidths;
                }
            } else {
                for (int i = minValue; i < (maxValue - removeValue); i++) {
                    rects.add(new Rectangle(startingX, 0, rectWidths, getHeight()));
                    startingX += rectWidths;
                }
            }

            for (Rectangle r : rects) {
                g2d.setColor(PROGRESS_COLOR);
                g2d.fillRect(r.x, r.y, r.width, r.height);
            }
        } else {//if indeterminate
            if (!indeterminateTimer.isRunning()) {
                indeterminateTimer.start();
            }
            g2d.setColor(PROGRESS_COLOR);
            if (!changeDirection) {
                if (x + 10 < getWidth() - (ballSize / 2)) {
                    x += 10;
                } else {
                    changeDirection = true;
                }
            } else if (changeDirection) {
                if (x + 10 > 0) {
                    x -= 10;
                } else {
                    changeDirection = false;
                }
            }
            g2d.fillOval(x, y, ballSize, getHeight());
        }
    }

    int getValue() {
        return currentValue;
    }

    public void setIndeterminate(boolean indeterminate) {
        this.indeterminate = indeterminate;
    }

    void setValue(int value) {
        if (value > maxValue) {
            return;
        }
        if (value < minValue) {
            return;
        }
        if (value < currentValue) {
            removeValue++;
        } else {
            int rem = value - currentValue;
            removeValue -= rem;
        }
        currentValue = value;
        repaint();
    }

    void setProgressColor(Color c) {
        PROGRESS_COLOR = c;
    }

    Color getProgressColor() {
        return PROGRESS_COLOR;
    }
}

2
myProgressBar.setBackground(Color.GREEN); 可能更好的写法是 myProgressBar.setBackground(new Color(0,255,0,127)); // 部分透明,因为它很可能会出现在一个活跃的游戏界面上。 - Andrew Thompson
1
@AndrewThompson +1 我更新了我的代码以适应上述情况,并添加了一些额外的功能,如 setIndeterminate(..)getValue(..),使其更像 JProgressBar :P - David Kroukamp
protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.gray); int width = getWidth(); g.fillRect(0, 0, width, 20); for (int i = maxValue; i > 0; --i) { int rectWidth = width / maxValue; int newWidth = width - rectWidth; g.setColor(Color.green); g.fillRect(0, 0, newWidth, 20); }你好,我借鉴了您的代码并进行了修改,但是我的代码似乎无法正常工作。您能否看出我在paint components中可能出错的地方? - KrazyKat89
@KrazyKat89,那段代码不够,你需要更多的代码。例如,if(!indeterminate)块中的所有内容,我建议你参加我的课程,在你的应用程序中让它正常工作,然后根据需要进行修改。 - David Kroukamp
@KrazyKat89 如果你想改变大小,就像我用虚拟 JPanel 一样覆盖 getPreferredSize,并返回适当的尺寸。 - David Kroukamp
显示剩余3条评论

0

这是我为我的游戏制作的东西。任何发现这个问题和我一样的人都可以自由地使用它。

尽情享受吧。

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;

import javax.swing.JPanel;

import com.jayavon.game.client.MyClient;

public class CustomJProgressBar extends JPanel {
    private static final long serialVersionUID = 1L;
    private Color color;
    private int width, height;
    private double minimum = 0.0;
    private double maximum = 100.0;
    private double value = 100.0;
    private Font font = MyClient.theme_font.deriveFont(Font.PLAIN, 12);

    public CustomJProgressBar(Color color) {
        super();
        this.color = color;
        setBounds(0, 0, width, height);
        setFont(font);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        //border
        g.setColor(MyClient.THEME_LIGHT_TEXT);
        g.drawRect(0, 0, getWidth()-1, getHeight()-1);

        //progress
        int drawAmount = (int) (((value - minimum) / (maximum - minimum)) * getWidth());
        g.setColor(color);
        g.fillRect(1, 1, drawAmount-2, getHeight()-2); //-2 to account for border

        //string painting
        String stringToPaint = (int)value + "/" + (int)maximum;
        Canvas c = new Canvas();
        FontMetrics fm = c.getFontMetrics(font);
        final int stringWidth = fm.stringWidth(stringToPaint);
        final int stringHeight = fm.getHeight();
        g.setColor(MyClient.THEME_LIGHT_TEXT);
        g.drawString(stringToPaint, (getWidth()/2) - (stringWidth/2), ((getHeight()/2) + (stringHeight/2))-2); //-2 to account for border
    }

    public void setColor(Color _color){
        this.color = _color;
    }

    public void setMinimum(double _minimum){
        this.minimum = _minimum;
    }

    public void setMaximum(double _maximum){
        this.maximum = _maximum;
    }

    public void setValue(double _value){
        this.value = _value;
    }
}

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