Java Swing 的圆形进度条无法工作

6

我发现了这个来自Oracle网站的测试项目,因为我想在我的项目中添加一个圆形进度条。

我正在使用Netbeans开发应用程序,但当我启动应用程序时,应该有圆形进度条的JPanel却消失了。

我已经删除了所有与解决此问题无关的代码,并得到了以下代码:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.plaf.LayerUI;


public class Loading_Test extends javax.swing.JFrame
{
    static final WaitLayerUI layerUI = new WaitLayerUI();
    public Loading_Test()
    {

        JPanel panel = new JPanel();
        JLayer<JPanel> jlayer = new JLayer<>(panel, layerUI);
        add(jlayer);

        initComponents();
    }

    @SuppressWarnings("unchecked")
    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        pack();
    }

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

            @Override
            public void run()
            {                
                JFrame frame = new Loading_Test();
                frame.setVisible(true);
                layerUI.start();
            }
        });
    }
}
class WaitLayerUI extends LayerUI<JPanel> implements ActionListener
{

    private boolean mIsRunning;
    private boolean mIsFadingOut;
    private Timer mTimer;
    private int mAngle;
    private int mFadeCount;
    private int mFadeLimit = 15;

    @Override
    public void paint(Graphics g, JComponent c)
    {
        int w = c.getWidth();
        int h = c.getHeight();

        // Paint the view.
        super.paint(g, c);

        if (!mIsRunning)
        {
            return;
        }

        Graphics2D g2 = (Graphics2D) g.create();

        float fade = (float) mFadeCount / (float) mFadeLimit;
        // Gray it out.
        Composite urComposite = g2.getComposite();
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f * fade));
        g2.fillRect(0, 0, w, h);
        g2.setComposite(urComposite);

        // Paint the wait indicator.
        int s = Math.min(w, h) / 5;
        int cx = w / 2;
        int cy = h / 2;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g2.setPaint(Color.white);
        g2.rotate(Math.PI * mAngle / 180, cx, cy);
        for (int i = 0; i < 12; i++)
        {
            float scale = (11.0f - (float) i) / 11.0f;
            g2.drawLine(cx + s, cy, cx + s * 2, cy);
            g2.rotate(-Math.PI / 6, cx, cy);
            g2.setComposite(AlphaComposite.getInstance(
                    AlphaComposite.SRC_OVER, scale * fade));
        }

        g2.dispose();
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        if (mIsRunning)
        {
            firePropertyChange("tick", 0, 1);
            mAngle += 3;
            if (mAngle >= 360)
            {
                mAngle = 0;
            }
            if (mIsFadingOut)
            {
                if (--mFadeCount == 0)
                {
                    mIsRunning = false;
                    mTimer.stop();
                }
            }
            else if (mFadeCount < mFadeLimit)
            {
                mFadeCount++;
            }
        }
    }

    public void start()
    {
        if (mIsRunning)
        {
            return;
        }

        // Run a thread for animation.
        mIsRunning = true;
        mIsFadingOut = false;
        mFadeCount = 0;
        int fps = 24;
        int tick = 1000 / fps;
        mTimer = new Timer(tick, this);
        mTimer.start();
    }

    public void stop()
    {
        mIsFadingOut = true;
    }

    @Override
    public void applyPropertyChange(PropertyChangeEvent pce, JLayer l)
    {
        if ("tick".equals(pce.getPropertyName()))
        {
            l.repaint();
        }
    }
}

如果您按照当前代码运行,您会遇到和我一样的问题,JPanel未被显示。但是,我发现如果我删除与布局相关的代码(27~36行),进度条就可以工作了。您还需要手动设置窗口大小。
setSize(300, 300);

之后

pack();

由于布局行是由Netbeans自动生成的,因此我正在尝试理解如何解决此问题,因为这些特定的代码行被Netbeans编辑器阻止编辑。


JLayer已添加到Java7中,对于Java6必须使用JXLayer代替(具有相同的效果,但具有更多功能)。如果您不太在意,那么有一些专家了解JLayer,他们使用Java7并使用paintComponent()方法。 - mKorbel
老实说,我不知道如何编辑WaitLayerUI中的“paint”方法。我从Oracle网站上将其“原样”拿来使用,它本身是可以工作的。但我遇到了上述问题。我已经为该项目设置了JDK6,它仍然可以正常工作。 - Deviling Master
你尝试过交换 initComponents 调用和 add(jlayer) 调用吗?如果你在 initComponents 中的调用与你已经添加到框架中的 JLayer 产生冲突,那我不会感到惊讶。 - Robin
@Robin 是的,我已经尝试过了,问题还是一样。getContentPane().setLayout(layout); 这行代码有问题。如果我注释掉它,就可以显示加载了。 - Deviling Master
我浪费了一天时间,但还是没能解决问题... 我会在JLabel上使用一个动画GIF... 故事结束! - Deviling Master
显示剩余2条评论
3个回答

4
我失去了一天时间,但还是不起作用...我会在JLabel上使用动态GIF...故事结束了!@Robin有很重要的信息,看起来你受到GuiBulder的限制,所以我不想发表评论,结果如下图所示,这是稍微修改过的代码。
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.plaf.LayerUI;

public class Loading_Test {

    static final WaitLayerUI layerUI = new WaitLayerUI();
    JFrame frame = new JFrame("JLayer With Animated Gif");

    public Loading_Test() {
        JPanel panel = new JPanel() {

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 300);
            }
        };
        JLayer<JPanel> jlayer = new JLayer<>(panel, layerUI);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(jlayer);
        frame.pack();
        frame.setVisible(true);
        layerUI.start();
    }

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

            @Override
            public void run() {
                Loading_Test loading_Test = new Loading_Test();

            }
        });
    }
}

class WaitLayerUI extends LayerUI<JPanel> implements ActionListener {

    private boolean mIsRunning;
    private boolean mIsFadingOut;
    private Timer mTimer;
    private int mAngle;
    private int mFadeCount;
    private int mFadeLimit = 15;

    @Override
    public void paint(Graphics g, JComponent c) {
        int w = c.getWidth();
        int h = c.getHeight();
        super.paint(g, c); // Paint the view.
        if (!mIsRunning) {
            return;
        }
        Graphics2D g2 = (Graphics2D) g.create();
        float fade = (float) mFadeCount / (float) mFadeLimit;
        Composite urComposite = g2.getComposite(); // Gray it out.
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f * fade));
        g2.fillRect(0, 0, w, h);
        g2.setComposite(urComposite);
        int s = Math.min(w, h) / 5;// Paint the wait indicator.
        int cx = w / 2;
        int cy = h / 2;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g2.setPaint(Color.white);
        g2.rotate(Math.PI * mAngle / 180, cx, cy);
        for (int i = 0; i < 12; i++) {
            float scale = (11.0f - (float) i) / 11.0f;
            g2.drawLine(cx + s, cy, cx + s * 2, cy);
            g2.rotate(-Math.PI / 6, cx, cy);
            g2.setComposite(AlphaComposite.getInstance(
                    AlphaComposite.SRC_OVER, scale * fade));
        }
        g2.dispose();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (mIsRunning) {
            firePropertyChange("tick", 0, 1);
            mAngle += 3;
            if (mAngle >= 360) {
                mAngle = 0;
            }
            if (mIsFadingOut) {
                if (--mFadeCount == 0) {
                    mIsRunning = false;
                    mTimer.stop();
                }
            } else if (mFadeCount < mFadeLimit) {
                mFadeCount++;
            }
        }
    }

    public void start() {
        if (mIsRunning) {
            return;
        }
        mIsRunning = true;// Run a thread for animation.
        mIsFadingOut = false;
        mFadeCount = 0;
        int fps = 24;
        int tick = 1000 / fps;
        mTimer = new Timer(tick, this);
        mTimer.start();
    }

    public void stop() {
        mIsFadingOut = true;
    }

    @Override
    public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) {
        if ("tick".equals(pce.getPropertyName())) {
            l.repaint();
        }
    }
}
  • 需要添加两个 JButtons,一个用于开始,另一个用于停止(展示和隐藏动画 Gig)。

  • 不要忘记 JPanel 已经实现了 FlowLayout,它很容易接受来自子组件的 PreferredSize


你没有像Netbeans一样使用GroupLayout。我知道对于简单的JFrame,所有的层都可以工作。但是如果你使用Netbeans编辑器创建一个JFrame,就会出现上述问题。 - Deviling Master
@Deviling Master,如果你想要做Java编程,就必须掌握Java、Swing和GuiBuilder。它们各自有着独立的生命周期。抱歉,我太懒了,我的懒惰无穷无尽,所以我留下了一些东西给鲸鱼……:-)。大部分都已经过时了,每个开发都在Sun变成Oracle的那一刻结束了,从Java7、SwingX、JavaFx等等。 - mKorbel
抱歉,我的英语不是很好。关于鲸鱼呢? - Deviling Master

4

我发现使用SwingLabs的Swing组件扩展可以提供更好、更简单(而且可行的)解决方案。

JXBusyLabel

对于NetBeans编辑器,有一个完全集成的插件,因此非常容易使用。


我添加了这个简单的教程来自定义图形:http://developerlife.com/tutorials/?p=248 - Deviling Master

1
从您的问题和评论中看来,您在Netbeans中实现JLayer时遇到了问题。我建议您使用CardLayout,它可以让您在普通的JPanel和带有旋转器的面板之间进行切换。
以下是一个简单的教程:使用Netbeans实现CardLayout

我的问题不在于JLayer本身,而是NetBeans默认设置的GroupLayout。CardLayout也有同样的问题:http://pastebin.com/CWzWJbKs。'card2'完全消失了,因为切换到card2的按钮不再起作用。但是如果你注释掉这一行`JLayer<JPanel> jlayer = new JLayer<>(card2, layerUI)`,然后尝试按下按钮,'card2'将会出现。 - Deviling Master

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