user1572522
user1572522

Reputation: 619

How to adjust graphics on Swing progress indicator?

The lower source code, with the pictured example, from the post Circular Progress Bar for Java Swing not working, is a great Swing feature.

I'd like to be able to use it with a "transparent" JFrame or glass pane but the graphic "petals", in paint(), want to interact with the background, so if the opacity of the background is very low, you can barely see the "petals". Not being familiar with the Graphics2D functions there, I've taken many stabs in the dark to try to adjust the code, but no luck, so could someone who knows how those functions work, suggest changes so that the "petals" don't interact with the background, and start out solid white, and gradually fade, as the code does?

I also don't need any fade-in or fade-out delays, and I'm also having difficulty with that, but if someone could just suggest modifications for the "petals", that would be great!

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

Upvotes: 1

Views: 561

Answers (1)

VGR
VGR

Reputation: 44414

One problem I see is that the code is setting the composite in the wrong place in the loop. It works, but as you've discovered, it's difficult to maintain or change.

g2.setComposite is being called at the end of the loop. This sets the alpha for the next petal drawn. This means there is no easy change you can make to adjust the alpha of the very first petal.

First, I would make the code more in line with the way humans think (at least, the way I think): Set the alpha of the line you're about to draw, right before you draw it:

for (int i = 0; i < 12; i++) {
    float scale = (12 - i) / 12f;
    g2.setComposite(AlphaComposite.getInstance(
            AlphaComposite.SRC_OVER, scale * fade));
    g2.drawLine(cx + s, cy, cx + s * 2, cy);
    g2.rotate(-Math.PI / 6, cx, cy);
}

Now, making it work with any arbitrary background alpha is easy. We merely adjust the value of scale:

float componentAlpha = 0.5f;

for (int i = 0; i < 12; i++) {
    float scale = (12 - i) / 12f;

    // Give petals the same relative alpha as the component
    // they're overlaying.
    scale *= componentAlpha;   

    g2.setComposite(AlphaComposite.getInstance(
            AlphaComposite.SRC_OVER, scale * fade));
    g2.drawLine(cx + s, cy, cx + s * 2, cy);
    g2.rotate(-Math.PI / 6, cx, cy);
}

Upvotes: 1

Related Questions