Circular Progress Bar for Java Swing not working
Asked Answered
R

3

6

i've discovered this test project from Oracle site because i want to add a circular progress bar in my project.

I'm developing the application with Netbeans, and when i start the application, the JPanel where the circle should be.... disappaer.

I've removed all the code that is not useful to solve this problem and i've got this code:

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();
        }
    }
}

If you run this code "as is", you should have the same my problem, the JPanel is not shown.

But i've discovered that if i remove the layout-related lines (27~36) the progress bar starts to work.

You also have to set manually the windows size adding

setSize(300, 300);

after

pack();

Because the layout-lines are automatically generated by Netbeans, i'm trying to understand how to solve this problem, because this particular lines of code are blocked for editing by Netbeans Editor.

Redwood answered 7/8, 2012 at 12:59 Comment(7)
I'm not on Java7 right now, can you please change paint() to the paintComponent(),Containment
Here the code written for JDK6: pastebin.com/ukwHX2PRRedwood
JLayer is added to Java7, for Java6 have to use JXLayer instead (then same effect, but with more_over funcioanlities), (maybe) don't care about, there are a few gurus that knows JLayer, use Java7 and with paintComponent() tooContainment
honestly, i don't know how to edit the "paint" method in WaitLayerUI. I took it "as is" from the Oracle site and by itself is working. But i have that problem specified above. I've set JDK6 for the project and it still workingRedwood
Have you tried swapping the initComponents call and the add(jlayer) call ? I wouldn't be surprised if your calls in the initComponents mess with the JLayer you already added to the frameBesiege
@Besiege Yes i've already tried, same problem. The line getContentPane().setLayout(layout); is the problem. If I comment it, the loading is shown.Redwood
I've lost one day and it still not working.... I'll use an animated gif on JLabel.... End of story!Redwood
C
4

I've lost one day and it still not working.... I'll use an animated gif on JLabel.... End of story!

@Robin has important info, and seems like as you limited by usage of GuiBulder..., then I'm don't want to comment something, result is

enter image description here

from little bit modified code

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();
        }
    }
}
  • have to add two JButtons, one for start and second for stop (show and hide animated Gig)

  • don't forger that JPanel has implemented FlowLayout, that pretty accepting PreferredSize came from its childs,

Containment answered 7/8, 2012 at 16:1 Comment(3)
You did not use GroupLayout like Netbeans does. I know that with simple JFrame all the layer works. But if you create a JFrame using netbeans editor there is the mentioned problemRedwood
@Deviling Master aaaach then you have to known Java, Swing and GuiBuilder, each of them lives with own life, sorry I'm too lazy, my endless lazyness, then I leave something for whales, .... :-), most of them is outdated, every development ended in the moment when Sun changed to Oracle, from Java7, SwingX, JavaFx .... etc.Containment
Sorry but me english is not very good. What about the whales?Redwood
R
4

Found a better simple (and working) solution with Swing Components Extension by SwingLabs

JXBusyLabel

There is a fully integrating plugin with NetBeans for its editor, so is very simple to use

Redwood answered 8/8, 2012 at 11:47 Comment(1)
I add this simple tutorial to customize the graphic: developerlife.com/tutorials/?p=248Redwood
B
1

It seems from your question and comments you are having problems implementing the JLayer in Netbeans, I'd suggest then rather going for a CardLayout which will allow you to switch between your normal JPanel and the one with the spinner.

Here is a simple tutorial: CardLayout With Netbeans

Boyish answered 7/8, 2012 at 14:16 Comment(1)
My problem is not with JLayer itself, but with the GroupLayout that NetBeans set by default. Same issue with the CardLayout:pastebin.com/CWzWJbKs . The card2 totally disappaer, because the button to switch to card2 does not work anymor. But if you comment this line JLayer<JPanel> jlayer = new JLayer<>(card2, layerUI) then you try the button, 'card2' will appaerRedwood

© 2022 - 2024 — McMap. All rights reserved.