Creating a Progress Dial

Articles —> Creating a Progress Dial

A program or web application often requires the need to indicate the progress of a background process to a user. A flashy way to go about indicating progress is via some type of animation, popular choice is the progress dial - similar to a clock, the bars (or hour-hands) of the clock light up as as things progress.

Hide ▼

Progress Dial

Having the need to customize progress dials in different context (size, color, and presentation media) - I decided a flexible method would be to create my own program to generate the progress dials. From there, I can incorporate the progress dials directly into an application (java based), or save the results as images to piece together into an animated gif image. The code itself is quite straightforward - albeit containing poorly designed hard-coded values together with a bit of cryptic syntax. The code relies on rotation of several rounded rects to correctly place them, with their colors defined by the position of the current 'time':


import java.awt.Color;

import java.awt.Dimension;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.RenderingHints;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.geom.AffineTransform;



import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.SwingUtilities;

import javax.swing.Timer;



/**

 * Progress Dial 

 * @author Greg Cope

 *

 */

public class Progress extends JPanel {

	

	private static final long serialVersionUID = 4321421L;

	int barCount = 12;

	int current = 0;//current activated bar

	private javax.swing.Timer timer = null;//animating timer

	

	/**

	 * Constructor that automatically creates a timer to animate the progress bar

	 */

	public Progress(){

		setBackground(Color.WHITE);

		setOpaque(true);

		timer = new Timer(60, new ActionListener(){



			@Override

			public void actionPerformed(ActionEvent e) {

				current++;

				if ( current > barCount - 1){

					current = 0;

				}

				repaint();

			}

			

		});

		timer.start();

	}

	

	@Override

	public void paintComponent(Graphics gr){

		Graphics2D g = (Graphics2D)gr;

		super.paintComponent(g);

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

		int width = getWidth() > getHeight() ? getWidth() : getHeight();//get the shortest dimensions

		int padding = (int)(width * 0.20d);

		AffineTransform at = new AffineTransform(g.getTransform());

		int center = width/2;

		int innerRadius = 10;

		int barHeight = 10;

		//get indexes for coloring the bars neighboring current

		int prev = current - 1 < 0 ? barCount - 1 : current - 1;

		int prevPrev = current - 2 >= 0 ? current - 2 : (current - 2 == -1 ? barCount - 1 : barCount - 2) ;

		for ( int i = 0; i < barCount; i++ ){

			double degrees = (i * 360d / barCount);

			g.setTransform(at);

			g.translate(center - innerRadius, center);

			g.rotate(Math.toRadians(degrees));

			//cryptic syntax below for brevity

			int opacity = i == current ? 255 : (i == prev ? 175 : (i == prevPrev ? 125 : 50));

			g.setColor(new Color(0,0,0,opacity));

			g.fillRoundRect(2*innerRadius,-barHeight/2, width / 2 - padding, barHeight, 10, 10);

		}

	}

	

	public static void main(String[] args) throws Exception{

		SwingUtilities.invokeAndWait(new Runnable(){

			@Override

			public void run() {

				Progress p = new Progress();

				JFrame frame = new JFrame();

				p.setPreferredSize(new Dimension(200,200));

				frame.add(p);

				frame.pack();

				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

				frame.setVisible(true);

			}

			

		});

	}

}

The nice thing about this code: just about any animated gif (see one above created using this exact code) can be constructed by saving every 'frame' as an image, and then stitching those images together as a gif in image processing software (rather than stitch together as a gif with java, I prefer image editing software for to allow for dithering in order to maintain the anti-alias affects).



There are no comments on this article.

Back to Articles


© 2008-2017 Greg Cope