Homework 5
This homework is designed to give you some practice with object modeling, composition,
and animation.
For this assignment you are going to write a shape interface and
4 shape classes. You will then complete an
already partially written GUI that uses them. The 4 classes
you will write and their API are as follows. You must strictly adhere
to this API in order for the partially written GUI that I have provided
to you to work. You are free to implement the classes any way you see
fit. For example, you do not have to literally declare
each property as an instance variable--you could for example use
a Java2D Rectangle object to store the position and size properties and
then implement methods that return these values individually):
- Shape: Shape should be an interface that defines the following methods:
- void draw(Graphics g): draws the shape on the screen using
that shape's properties
- boolean contains(int x, int y): true if the shape
contains the point and false otherwise. You should use
the shape's bounding box to determine whether or not the
shape contains the point.
- int getLeft(): returns the value of the left side of the shape
- int getTop(): returns the value of the top side of the shape
- int getWidth(): returns the width of the shape
- int getHeight(): returns the height of the shape
- AbstractShape: Your RectShape and TextShape classes will be composed from
this class. In other words, rather than inheriting from this class, your
RectShape and TextShape classes will declare an instance of AbstractShape
and delegate the appropriate part of their implementation to the AbstractShape.
This class should not extend any class and it should
have the following properties and methods:
- Properties
- int left, top: The upper, left corner of the shape
- int width, height: The size of the shape
- Color fillColor: The fill color of the shape
- Color lineColor: The boundary color of the shape
- boolean filled: True if the shape should be filled and
false otherwise
- boolean border: True if the shape should have a border
and false otherwise
- Methods:
- boolean contains(int x, int y): true if the shape
contains the point and false otherwise. You should use
the shape's bounding box to determine whether or not the
shape contains the point.
- int getLeft(): returns the value of the left property
- int getTop(): returns the value of the top property
- int getWidth(): returns the value of the width property
- int getHeight(): returns the value of the height property
- Color getFillColor(): returns the fill color of the shape
- Color getLineColor(): returns the boundary color of the
shape
- boolean getFilled(): returns the value of the filled property
- boolean getBorder(): returns the value of the border property
- void setLeft(int): sets the value of the left property
- void setTop(int): sets the value of the top property
- void setWidth(int): sets the value of the width property
- void setHeight(int): sets the value of the height property
- void setFillColor(Color): sets the fill color of the shape
- void setLineColor(Color): sets the boundary color of the
shape
- void setFilled(boolean): sets the value of the filled property
- void setBorder(boolean): sets the value of the border property
- RectShape: This class represents a rectangle and is capable of
drawing a rectange on the screen.
- RectShape should implement Shape
and should not extend any class.
- You will be tempted to have a RectShape
inherit from an AbstractShape but you should not do so. Instead you
should use composition and have a RectShape contain an instance of
an AbstractShape. The reason for using composition rather than
inheritance is that in a real system, you would want to maintain the
flexibility to narrow a RectShape's API in the future, and you could not
do this if you inherited from AbstractShape.
RectShape
should have the following
properties and methods:
- Properties: It should have all the properties implemented
by an AbstractShape. Please note that you do not have to
implement these properties using instance variables. You can
implement these properties by using the AbstractShape to
store them.
- Methods: It should provide all the properties implemented by
an AbstractShape plus the following draw method:
- void draw(Graphics g): this method should draw a
rectangle. The rectangle should be filled with the fill color if the
filled property is true, and the rectangle should have a border
drawn in the line color if the border property is true.
- TextShape: This class represents a piece of text that should be
drawn on the screen.
- It should implement the Shape interface but
not extend any class.
- Like RectShape, it should
be composed from an AbstractShape.
Notice that its API is narrower than that of an AbstractShape. Specifically
it omits setWidth and setHeight, since the width and height properties
should be read-only variables, and it also omits the border
property, because text should always be rendered.
TextShape should
define the following properties and methods:
- Properties: Note that some of these properties, such as label and
font, are not implemented in an AbstractShape object and hence you
will need to declare them as instance variables. Conversely, other
properties, such as width and height, are implemented in an
AbstractShape object, and you can use that object to store those
properties.
- int width, height: These properties should be read-only and
should be calculated from the text shape's string and
font.
- String label: The text shape's string
- Font font: The text shape's font.
- Color fillColor: The background fill color of the TextShape
- Color lineColor: The color of the text
- boolean filled: True if the shape should be filled with the
background fill color and
false otherwise
- Methods
- void draw(Graphics g): Draws the text shape, with the
upper left corner of the TextShape corresponding to the
left and top of the TextShape. For example, if the left and
top are (0,0), then I want the TextShape's upper left corner to
start at (0,0). I do not want the TextShape's baseline to
start at 0, which means your draw method must adjust the value
it gives to Java's g.drawString command so that the baseline
is drawn in a way that allows the text to appear as I have described.
If the filled
property is true, then the text should be drawn on top of
a filled rectangle drawn in the text's fill color. The rectangle
should be the smallest rectangle that completely contains the
text shape (i.e., its left and top should be equal to the
TextShape's left and top and its width and height should be equal
to the TextShape's width and height). The TextShape's line color
should determine the color in which the text is rendered.
- Get/Set methods for each of the properties, except for width
and height, which should be read-only and thus only have
get methods. Follow the same
naming convention
used for AbstractShape, where get and set are lowercase
and the first character of the property is uppercased
(i.e., getFont, getLabel, setFont, setLabel).
- int getWidth(), int getHeight(): These methods should
use a FontMetric object to calculate the width and height of
the current string. You can use the command:
Toolkit.getDefaultToolkit().getFontMetrics(Font f)
to get a FontMetrics object to measure the width and height of a
string that is rendered in a Font f. You will be able to
use this FontMetrics object before a TextShape is attached to a
window (i.e., added to a CanvasShape, which is in turn added to
a JPanel). In general this is poor technique because you should only
try to position objects at rendering time, which in Java speak means
when paintComponent gets called. You will get a warning about using
a deprecated method--that is okay. I am allowing you to use this
command for this assignment because you already have a significant
task in trying to understand how to do object modeling, and having
you defer positioning until rendering time would add unneeded complexity
to the assignment.
- CanvasShape: This class should extend a JPanel. It should not implement
Shape.
- Properties
- Vector<Shape> shapes: a list of the shapes the canvas is supposed
to draw.
- Methods
- Vector<Shape> getShapes(): returns a list of the
shapes that have been registered with this canvas.
- void add(Shape): Adds a shape to the canvas
- void paintComponent(Graphics g): iterates through the
shapes that have been registered with the CanvasShape
and calls each of their draw methods.
Note that a CanvasShape does not have width and height properties. That
is because you can use a JPanel's getPreferredSize() method to
define the desired width and height of the CanvasShape.
Graphical Interface to Create
You are going to complete a graphical interface that displays a set of
colored labeled boxes.
Your interface should have the following properties:
- The main application window will have a collection of labeled boxes.
The labeled boxes should be rectangles with text shapes inscribed in
them. The LabeledBox class has already
been written for you to show you an example of object modeling. It can
be found in ~bvz/gui/hw/hw5/src/LabeledBox.java. The main application
window will be found in the lower right side of the application.
- There are three control panels surrounding the main application
window. These three control panels allow the user to:
- change the font of the currently selected labeled box (upper left
control panel),
- create a new labeled box (lower left control panel), and
- animatedly move the currently selected labeled box to a new
destination (upper right control panel).
- The control panel for creating a new labeled box contains
a set of widgets for setting the properties of the new labeled
box and a button for creating a new labeled box. Most of this control
panel has been
written for you and can be found in
~bvz/gui/hw/hw5/src/LabelBoxControls.java. If you
search for RectShape, CanvasShape, and LabeledBox, you can find
the code that I wrote to create a new labeled box and to
change the color of the color chip that you will see in this window.
This file contains some useful examples of how you can create a
LabeledBox and a RectShape, and how you can set properties in these
objects using the API for these objects.
The controls that you will see in this region include:
- A color chip that displays the color
that will be used to fill a new labeled box. When the user
clicks with the mouse button over the color chip, the
action associated with the mouse button displays
a JColorChooser object that allows
the user to select a new fill color. The color chip
reflects this new fill color. The color chip
is a RectShape and it
is added to one of your canvas shapes. Since the canvas shape
is a JPanel, my code adds the canvas shape containing the
color chip
to the LabeledBoxControls region in the way I normally would
with any Java Swing widget. Also since the canvas shape is
exactly as large as the color chip, I can attach a MouseListener
to the CanvasShape that listens for mouse events and pops up
the JColorChooser object when appropriate.
- Text boxes to specify the left and top of the labeled
box.
- A text box for entering the label
- A command button for creating a new labeled rectangle
using the information gathered from the other widgets.
- The topmost region of the application (i.e., the North region of the
window) is divided
into a leftmost control panel that has
a set of widgets for setting the font of the currently selected labeled box
and a rightmost control panel that has a set of widgets for
animatedly moving
the currently selected
labeled box around the main application window.
- Font Controls
- There are radio buttons for specifying the font size
as small, medium, or large. The font sizes corresponding to
these sizes should be 8, 12, and 24.
- The font controls should be disabled if no labeled box is
selected, to prevent the user from making an error.
- When a labeled box is selected, the radio button corresponding
to the box's font size should be selected. When no box is
selected, it is ok to let the last selected radio button remain
selected.
- Animation Controls
- There are text boxes for specifying the destination left
and top locations of the labeled box.
- There is a slider for specifying the number of seconds
that the animation should take. The animation should take from
1-10 seconds. Your animation may take a bit longer because
Swing timers will drift.
- There is a command button for invoking the animation. When
the command button is selected, its actionPerformed method
should create and start a Swing timer object that animatedly
moves the currently selected labeled box from its current
location to its new location. You should use a 25 frames per
second animation rate.
- The animation controls should be disabled if no labeled box
is selected, to prevent the user from making an error.
- A labeled box may be selected by clicking on it with the mouse. If
another labeled box was previously selected, it should become de-selected.
If the mouse is clicked over an empty region of the canvas, then any
previously selected labeled box should be de-selected. Selected labeled
boxes should be given a blue border, become unfilled, and render their
text in white (the text background should still be black). For this
assignment you should attach a mouse listener to the Canvas Shape that
represents the drawing panel and
iterate through the canvas's shapes, querying each one in turn, to
determine whether or not they contain the mouse point. If they contain
the mouse point, then you should select them.
- The control panel for creating a labeled box can alter a number of
the properties of a labeled box. The remaining properties should
have the following default values:
- The text should be rendered in yellow, unless its labeled
box is selected, in which case it should be rendered in white.
- The text should be a sans-serif font with a plain style. The
default font size will be medium (12) but can be changed
by the font controls.
- The text background should be black
- A labeled box's border should be black, unless it is selected,
in which case it should be blue.
My code for creating a labeled box will assign the appropriate default
values to a labeled box but when you de-select a labeled box, you will
need to restore these default values.
What Has Been Done For You
The following classes have already been created for you in
~bvz/gui/hw/hw5/src:
- LabeledBoxApplication.java: This class is the top-level class for
your application. It creates a JFrame, then creates
instances of each of the control panels
and the main application panel, adds them to the JFrame, and makes
the JFrame visible.
- LabeledBox.java: This class creates a labeled box that consists of
a RectShape and a TextShape. If you look at the code for the class,
you can see which properties have been defined for a labeled box.
What Has Been Partially Done For You
The following classes in
~bvz/gui/hw/hw5/src have been started for you but you need to complete them:
- LabelBoxControls.java: This class creates the control panel that allows
the user to set the properties for a new labeled box and to create
a labeled box. You need to add code to this class to do your own layout of
the widgets (search for the comment "Your layout code goes here").
- DrawPanel.java: This class creates the main application panel. You will
need to add the following functionality:
- The ability to analyze a mouse click and either 1) select a
labeled box if the mouse is over a labeled box and deselect
any previously selected labeled box, or 2) deselect any previously
selected labeled box if the mouse is not over any labeled box.
- The ability to change the properties of a labeled box when it
is either selected or deselected, and the ability to notify interested
control panels (i.e., the font and animation control panels) when
an object is either selected or deselected.
What You Need To Write From Scratch
The following classes and interfaces you will need to write from scratch:
- Shape.java (interface)
- AbstractShape.java
- RectShape.java
- TextShape.java
- CanvasShape.java
- FontControls.java: The font control panel. The constructor should take
a reference to the main drawing panel (see LabeledBoxApplication.java for
the constructor call). You will need a reference to the main drawing panel
in order to determine the currently selected labeled box. It is okay to
create this class with an IDE if you want.
- AnimationControls.java: The animation control panel. The constructor should take
a reference to the main drawing panel (see LabeledBoxApplication.java for
the constructor call). You will need a reference to the main drawing panel
in order to determine the currently selected labeled box. It is okay to
create this class with an IDE if you want.
What to Submit
Submit a jar file named LabeledBox.jar with the following files:
- All of the .java files mentioned above, even the ones I completed for
you.
- A manifest.txt file that indicates that LabeledBoxApplication is your main class.
- All your class files.
We will expect to be able to execute your jar file by typing:
java -jar LabeledBox.jar