Incdec3

Incdec.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * Abstract base class for all IncDec's
 * with specialized stuff factored into separate overloadable methods
 */
abstract public class Incdec extends JPanel implements ActionListener {
    /**
     * Our internal components that we need to remember
     */
    protected JButton upButton, downButton;
    protected JComponent show; //*1 Generalize for subclasses

    /**
     * Our data, initialized inline here 
     */
    protected int value = 0;
    protected int min = 0;
    protected int max = 100;
    protected int incr = 1;

    /**
     * Only those aspects of initialization that everbody will want
     */
    protected Incdec () {
	// Put a border on our JPanel
	// default color, user can modify later
	setBorder (new LineBorder(Color.RED, 1));
    }

    /**
     * Pieces of old constructor, factored out here for subclasses to use
     */
    protected void makeUp () {
	upButton = new JButton ("+");
	upButton.addActionListener (this);
	add (upButton);
     }

    protected void makeDown () {
	downButton = new JButton ("-");
	downButton.addActionListener (this);
	add (downButton);
    }
    
    /**
     * We provide the more popular version here,
     * subclasses can use or overload,
     * their widget might not be a label
     */
    protected void makeShow () {
	show = new JLabel (); //*2 Common case here, later subclasses overload
	((JLabel)show).setHorizontalAlignment (JLabel.CENTER); //*2
	refreshShow (); //*2
	add (show); //*2
    }

    /**
     * Common callback for both buttons,
     * we use ivar to disambiguate.
     */
    public void actionPerformed (ActionEvent e) {
        if (e.getSource()==upButton) value += incr;
	else if (e.getSource()==downButton) value -= incr;

	// Clamp at bounds
	if (value > max) value = max;
	if (value < min) value = min;

	// Update our widget to match
	refreshShow ();
    }

    /**
     * Common code fragment, extracted to here.
     * subclasses might not be using a label
     */
    protected void refreshShow() { //*3 Common case here also, later subclasses overload
	((JLabel)show).setText (String.valueOf (value)); //*3
	repaint (); //*3
    }

    /**
     * Just as an example, we let user customize border color here
     */
    public void setBorderColor (Color color) {
	setBorder (new LineBorder(color, 1));
	repaint ();
    }

    public int getValue() { return value; }
}

IncdecPie.java  [download]

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Code for subclass IncDecPie
 */
public class IncdecPie extends Incdec {
    final static double circleSize = 50.;

    /**
     * Superclass constructor does most of the work, we finish the job here,
     * just choose the layout and the order of the 3 widgets
     */
    public IncdecPie () {
	setLayout (new GridLayout (3, 1));
	makeUp ();
	makeShow (); //*1 We overload makeShow and refreshShow
	makeDown ();
    }

    /**
     * Overload base class version
     */
    protected void makeShow () { //*1
	show = new MyCanvas (this); //*2 show is still a JComponent
	add (show);
	// Don't call refreshShow(), first paint callback will call it
    }

    /**
     * Overload base class version
     */
    protected void refreshShow () { //*1
	show.repaint (); //*1
    }

    /**
     * Paint callback from our canvas, passed up to us,
     * we redraw from our saved value data
     */
    public void drawCanvas (Graphics g) {
	Graphics2D g2 = (Graphics2D) g;

	// Outline
	g2.draw (new Ellipse2D.Double (0., 0., circleSize, circleSize)); //*3 Draw outline, then filled in segment, using "value"

	// Filled in segment
	g2.fill (new Arc2D.Double (0., 0., circleSize, circleSize, 0., 360. * (value-min) / (max-min), Arc2D.PIE)); //*3
    }
}

IncdecVert.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Code for subclass Incdecvert
 */
public class IncdecVert extends Incdec {
    /**
     * Superclass constructor does most of the work, we finish the job here,
     * just choose the layout and the order of the 3 widgets
     */
    public IncdecVert () {
	setLayout (new GridLayout (3, 1));
	makeUp ();
	makeShow (); //*1 Same as Incdec2 except "show" not "label"
	makeDown ();
    }
}

MyCanvas.java  [download]

import java.awt.*;
import javax.swing.*;

/**
 * Need our own little class for the pie chart,
 * so it can catch paint callback and pass it back to parent
 */
public class MyCanvas extends JPanel {
    private IncdecPie parent;

    public MyCanvas (IncdecPie parent) {
	this.parent = parent;
	setPreferredSize (new Dimension (50, 50));
    }
    
    public void paintComponent (Graphics g) {
	super.paintComponent(g);
	parent.drawCanvas (g); //*1 Catch this and delegate to parent, it has the info
    }
}

Main.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Main program for Incdec
 */
public class Main extends JFrame implements ActionListener {
    public static void main (String [] args) {
	java.awt.EventQueue.invokeLater (new Runnable() {
            public void run() {
		new Main ();
            }
        });
    }

    private Incdec id1, id2, id3, id4;

    public Main () {
	// Window setup
	setSize (600, 300);
	Container content = getContentPane();
	content.setLayout (new FlowLayout());
	setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

	// Create some new Incdec's
	id1 = new IncdecVert ();
	content.add (id1);

	id2 = new IncdecPie ();
	content.add (id2); //*1 Test the new class, same API

	id3 = new IncdecVert ();
	content.add (id3);

	id4 = new IncdecPie ();
	content.add (id4);

	// And a button to report the data from the Incdec's, for testing
	JButton b = new JButton ("Get data");
	b.addActionListener (this);
	content.add (b);

	setVisible (true);
    }

    /**
     * Callback from the test button
     */
    public void actionPerformed (ActionEvent e) {
	System.out.println ("Value 1 = " + id1.getValue() + //*1
		", Value 2 = " +  id2.getValue() + //*1
		", Value 3 = " +  id3.getValue() + //*1
		", Value 4 = " +  id4.getValue()); //*1
    }
}