| 1.124 Lecture 14 | 10/26/2000 |
So far, we have seen user interface components that display static content. The individual components posessed sufficient knowledge to draw themselves and so we did not have to do anything special beyond creating the components and describing their layout. If a component is obscured by some other window and then uncovered again, it is the job of the window system to make sure that the component is properly redrawn.
There are instances, however, where we will want to change the appearance of a component e.g. we may wish to draw a graph, display an image or even display an animation within the component. This requires the use of custom painting code. The recommended way to implement custom painting is to extend the JPanel class. We will need to be concerned with two methods:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Main {
public static void main(String[] args0) {
JFrame frame = new JFrame();
frame.setSize(400, 400);
DrawingArea drawingArea
= new DrawingArea();
frame.getContentPane().add(drawingArea);
frame.addWindowListener(new
WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
}
class DrawingArea extends JPanel {
private String mText;
private static String mStr1 = "The mouse was
pressed here!";
private static String mStr2 = "The mouse was
released here!";
private int miX, miY;
// The constructor simply registers the drawing
area to receive mouse events from itself.
public DrawingArea() {
addMouseListener(new
MouseAdapter() {
public void mousePressed(MouseEvent e) {
miX = e.getX();
miY = e.getY();
mText = mStr1;
repaint();
}
public void mouseReleased(MouseEvent e) {
miX = e.getX();
miY = e.getY();
mText = mStr2;
repaint();
}
});
}
// The paint method. This gets called in
response to repaint().
public void paintComponent(Graphics g) {
super.paintComponent(g);
// This paints the background.
if (mText != null)
g.drawString(mText, miX, miY);
}
}
Note that prior to the introduction of the Swing package, one would
override the paint() method to implement custom painting.
In Swing applications, however, we override the paintComponent()
method instead. The paintComponent() method will be called
by the paint() method in class JComponent. The JComponent's
paint()
method also implements features such as double buffering, which are useful
in animation.
This program illustrates how to draw some basic shapes.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Main {
public static void main(String[] args0) {
JFrame frame = new JFrame();
frame.setSize(400, 400);
DrawingArea drawingArea
= new DrawingArea();
frame.getContentPane().add(drawingArea);
frame.addWindowListener(new
WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
}
class DrawingArea extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw some simple geometric
primitives.
g.setColor(Color.red);
g.drawLine(10, 10, 40,
50);
// x1, y1, x2, y2
g.setColor(Color.green);
g.drawRect(100, 100,
40, 30);
// x, y, width, height
g.setColor(Color.yellow);
g.drawOval(100, 200,
30, 50);
// x, y, width, height
g.setColor(Color.blue);
g.drawArc(200, 200, 50,
30, 45, 90);
// x, y, width, height, start angle, arc angle
int x1_points[] = {100,
130, 140, 115, 90};
int y1_points[] = {300,
300, 340, 370, 340};
g.setColor(Color.black);
g.drawPolygon(x1_points,
y1_points, x1_points.length); // x array, y array, length
int x2_points[] = {300,
330, 340, 315, 290};
int y2_points[] = {300,
300, 340, 370, 340};
g.setColor(Color.cyan);
g.drawPolyline(x2_points,
y2_points, x2_points.length); // x array, y array, length
g.setColor(Color.orange);
g.fillRect(300, 100,
40, 30);
// x, y, width, height
g.setColor(Color.magenta);
g.fill3DRect(300, 200,
40, 30, true);
// x, y, width, height, raised
}
}
The Java
2D API provides a range of advanced capabilities, such as stroking
and filling, affine transformations, compositing and transparency.
// This is a Java graphics example that can be run either as an applet
or as an application.
// Created by Kevin Amaratunga 10/17/1997. Converted to Swing
10/17/1999.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
// In order to run as an applet, class Geometry must be declared
as a public class. Note that there
// cannot be more than one public class in a .java file.
Also, the public class must have the same
// same name as the .java file.
public class Geometry extends JApplet {
JTextArea mTextArea;
DrawingArea mDrawingArea;
public Geometry() {
// Get the applet's container.
Container c = getContentPane();
// Choose a layout manager.
BorderLayout is a straightforward one to use.
c.setLayout(new BorderLayout());
// Create a drawing area and
add it to the center of the applet.
mDrawingArea = new DrawingArea(this);
c.add("Center", mDrawingArea);
// Create a read only text area
to be used for displaying
// information. Add it
to the bottom of the applet.
mTextArea = new JTextArea();
JScrollPane scrollPane = new
JScrollPane(mTextArea);
scrollPane.setPreferredSize(new
Dimension(600, 100));
mTextArea.setEditable(false);
c.add("South", scrollPane);
}
public JTextArea getTextArea() {
return mTextArea;
}
public static void main(String args[]) {
// Create the applet
object.
Geometry geomApplet =
new Geometry();
// Create a frame.
Then set its size and title.
JFrame frame = new JFrame();
frame.setSize(600, 600);
frame.setTitle(geomApplet.getClass().getName());
// Make the frame closable.
WindowListener listener
= new WindowAdapter() {
// An anonymous class that extends WindowAdapter.
public void windowClosing(WindowEvent e) {
System.out.println("Window closing");
System.exit(0);
}
};
frame.addWindowListener(listener);
// Add the applet to the
center of the frame.
frame.getContentPane().add("Center",
geomApplet);
// Initialize the applet.
geomApplet.init();
// Make the frame visible.
frame.setVisible(true);
// Start the applet.
geomApplet.start();
}
}
// The drawing area is the area within all the objects will be drawn.
class DrawingArea extends JPanel implements MouseListener {
// Parent and child widgets.
Geometry mGeomApplet;
// The parent applet.
JPopupMenu mPopupMenu;
// Popup menu for creating new objects.
// Object lists.
Vector mPointList;
// List of all Point objects.
Vector mLineList;
// List of all Line objects.
Vector mPolygonList;
// List of all Polygon objects.
// Constants that indicate which kind of object
(if any) is currently being created.
static final int NO_OBJECT = 0;
static final int POINT_OBJECT = 1;
static final int LINE_OBJECT = 2;
static final int POLYGON_OBJECT = 3;
// Miscellaneous state variables.
int miLastButton = 0;
// Last button for which an event was received.
int miAcceptingInput = 0;
// Type of object (if any) that we are currently creating.
int miPointsEntered = 0;
// Number of points entered for this object so far.
Object mCurrentObject = null;
// The object that we are currently creating.
// DrawingArea constructor.
DrawingArea(Geometry geomApplet) {
JMenuItem menuItem;
mGeomApplet = geomApplet;
// Set the background
color.
setBackground(Color.white);
// Register the drawing
area to start listening to mouse events.
addMouseListener(this);
// Create a popup menu
and make it a child of the drawing area, but don't show it just yet.
mPopupMenu = new JPopupMenu("New
Object");
menuItem = new JMenuItem("Point");
menuItem.addActionListener(new
PointActionListener(this));
mPopupMenu.add(menuItem);
menuItem = new JMenuItem("Line");
menuItem.addActionListener(new
LineActionListener(this));
mPopupMenu.add(menuItem);
menuItem = new JMenuItem("Polygon");
menuItem.addActionListener(new
PolygonActionListener(this));
mPopupMenu.add(menuItem);
add(mPopupMenu);
// Create the object lists
with a reasonable initial capacity.
mPointList = new Vector(10);
mLineList = new Vector(10);
mPolygonList = new Vector(10);
}
// The paint method.
public void paintComponent(Graphics g) {
int i;
// Paint the background.
super.paintComponent(g);
// Draw all objects that
are stored in the object lists.
for (i = 0; i < mPointList.size();
i++) {
Point point = (Point)mPointList.elementAt(i);
g.fillRect(point.x-1, point.y-1, 3, 3);
}
for (i = 0; i < mLineList.size();
i++) {
Line line = (Line)mLineList.elementAt(i);
line.draw(g);
}
for (i = 0; i < mPolygonList.size();
i++) {
Polygon polygon = (Polygon)mPolygonList.elementAt(i);
int j;
g.setColor(Color.red);
g.drawPolygon(polygon);
g.setColor(Color.black);
for (j = 0; j < polygon.npoints; j++) {
g.fillRect(polygon.xpoints[j], polygon.ypoints[j], 3, 3);
}
}
// Draw as much of the
current object as available.
switch (miAcceptingInput)
{
case LINE_OBJECT:
Line line = (Line)mCurrentObject;
if (line.mb1 && !line.mb2)
g.fillRect(line.mEnd1.x-1, line.mEnd1.y-1, 3, 3);
break;
case POLYGON_OBJECT:
Polygon polygon = (Polygon)mCurrentObject;
int j;
g.setColor(Color.red);
g.drawPolyline(polygon.xpoints, polygon.ypoints, polygon.npoints);
g.setColor(Color.black);
for (j = 0; j < polygon.npoints; j++) {
g.fillRect(polygon.xpoints[j], polygon.ypoints[j], 3, 3);
}
break;
default:
break;
}
// Draw some text at the
top of the drawing area.
int w = getSize().width;
int h = getSize().height;
g.drawRect(0, 0, w -
1, h - 1);
g.setFont(new Font("Helvetica",
Font.PLAIN, 15));
g.drawString("Drawing
area", (w - g.getFontMetrics().stringWidth("Drawing area"))/2, 10);
}
// The next five methods are required, since we
implement the
// MouseListener interface. We are only
interested in mouse pressed
// events.
public void mousePressed(MouseEvent e) {
int iX = e.getX();
// The x and y coordinates of the
int iY = e.getY();
// mouse event.
int iModifier = e.getModifiers();
if ((iModifier & InputEvent.BUTTON1_MASK)
!= 0) {
miLastButton = 1;
// If we are currently accepting input for a new object,
// then add the current point to the object.
if (miAcceptingInput != NO_OBJECT)
addPointToObject(iX, iY);
}
else if ((iModifier &
InputEvent.BUTTON2_MASK) != 0) {
miLastButton = 2;
}
else if ((iModifier &
InputEvent.BUTTON3_MASK) != 0) {
miLastButton = 3;
if (miAcceptingInput == NO_OBJECT) {
// Display the popup menu provided we are not accepting
// any input for a new object.
mPopupMenu.show(this, iX, iY);
}
else if (miAcceptingInput == POLYGON_OBJECT) {
// If current object is a polygon, finish it.
mPolygonList.addElement(mCurrentObject);
String str = "Finished creating polygon object.\n";
mGeomApplet.getTextArea().append(str);
mGeomApplet.repaint();
miAcceptingInput = NO_OBJECT;
miPointsEntered = 0;
mCurrentObject = null;
}
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void getPointInput() {
miAcceptingInput = POINT_OBJECT;
mCurrentObject = (Object)new
Point();
mGeomApplet.getTextArea().append("New
point object: enter point.\n");
}
public void getLineInput() {
miAcceptingInput = LINE_OBJECT;
mCurrentObject = (Object)new
Line();
mGeomApplet.getTextArea().append("New
line: enter end points.\n");
}
public void getPolygonInput() {
miAcceptingInput = POLYGON_OBJECT;
mCurrentObject = (Object)new
Polygon();
mGeomApplet.getTextArea().append("New
polygon: enter vertices ");
mGeomApplet.getTextArea().append("(click
right button to finish).\n");
}
void addPointToObject(int iX, int iY) {
String str;
miPointsEntered++;
switch (miAcceptingInput)
{
case POINT_OBJECT:
str = "Point at (" + iX + "," + iY + ")\n";
mGeomApplet.getTextArea().append(str);
Point point = (Point)mCurrentObject;
point.x = iX;
point.y = iY;
mPointList.addElement(mCurrentObject);
str = "Finished creating point object.\n";
mGeomApplet.getTextArea().append(str);
mGeomApplet.repaint();
miAcceptingInput = NO_OBJECT;
miPointsEntered = 0;
mCurrentObject = null;
break;
case LINE_OBJECT:
if (miPointsEntered <= 2) {
str = "End " + miPointsEntered + " at (" + iX + "," + iY + ")";
str += "\n";
mGeomApplet.getTextArea().append(str);
}
Line line = (Line)mCurrentObject;
if (miPointsEntered == 1) {
line.setEnd1(iX, iY);
mGeomApplet.repaint();
}
else {
if (miPointsEntered == 2) {
line.setEnd2(iX, iY);
mLineList.addElement(mCurrentObject);
str = "Finished creating line object.\n";
mGeomApplet.getTextArea().append(str);
mGeomApplet.repaint();
}
miAcceptingInput = NO_OBJECT;
miPointsEntered = 0;
mCurrentObject = null;
}
break;
case POLYGON_OBJECT:
str = "Vertex " + miPointsEntered + " at (" + iX + "," + iY + ")";
str += "\n";
mGeomApplet.getTextArea().append(str);
Polygon polygon = (Polygon)mCurrentObject;
polygon.addPoint(iX, iY);
mGeomApplet.repaint();
break;
default:
break;
}
// End switch.
}
}
// Action listener to create a new Point object.
class PointActionListener implements ActionListener {
DrawingArea mDrawingArea;
PointActionListener(DrawingArea drawingArea) {
mDrawingArea = drawingArea;
}
public void actionPerformed(ActionEvent e) {
mDrawingArea.getPointInput();
}
}
// Action listener to create a new Line object.
class LineActionListener implements ActionListener {
DrawingArea mDrawingArea;
LineActionListener(DrawingArea drawingArea) {
mDrawingArea = drawingArea;
}
public void actionPerformed(ActionEvent e) {
mDrawingArea.getLineInput();
}
}
// Action listener to create a new Polygon object.
class PolygonActionListener implements ActionListener {
DrawingArea mDrawingArea;
PolygonActionListener(DrawingArea drawingArea)
{
mDrawingArea = drawingArea;
}
public void actionPerformed(ActionEvent e) {
mDrawingArea.getPolygonInput();
}
}
// A line class.
class Line {
Point mEnd1, mEnd2;
boolean mb1, mb2;
Line() {
mb1 = mb2 = false;
mEnd1 = new Point();
mEnd2 = new Point();
}
void setEnd1(int iX, int iY) {
mEnd1.x = iX;
mEnd1.y = iY;
mb1 = true;
}
void setEnd2(int iX, int iY) {
mEnd2.x = iX;
mEnd2.y = iY;
mb2 = true;
}
void draw(Graphics g) {
g.fillRect(mEnd1.x-1,
mEnd1.y-1, 3, 3);
g.fillRect(mEnd2.x-1,
mEnd2.y-1, 3, 3);
g.setColor(Color.green);
g.drawLine(mEnd1.x, mEnd1.y,
mEnd2.x, mEnd2.y);
g.setColor(Color.black);
}
}