Swapped with
X4 - Nodenet
In this lab, you will be programming
a (primitive) visual drawing device. You will do so without using any special
libraries, not even cs101, but. Rely on the java standard library only.
Finally, you will add this lab to
your workshop result (the "Divelog"
application)
Objectives: GUIs and GUI event
handling.
Directly related to: Lectures 11a
and 11b; less directly: Chapters 15 and 16
Begin with trying Scribble: scribble.jar
In order to draw into a GUI, we need the class java.awt.Graphics.
A Graphics object attached to a component administers its pixel matrix. For a
Container Component, that is just an empty area with a white background. A
Component inside this Container has its own Grahics
object describing its shape, color etc. The Component
method paint()
displays the component onto the screen. It is called by the VM whenever a
redrawing is required (e.g. after resizing or being covered by a popup window).
Here is a small list of the most important
Graphics drawing methods that can be used to add elements to a Component’s
appearance – open the API documentation for Graphics and Component while
reading these notes!
public void drawLine(int x1, int y1, int x2, int y2); // line of current color
public void drawRect(int x, int y, int
width, int height);
// rectangle
public void drawOval(int x, int y, int width, int height); //
ellipse within rectangle positions
public void drawString(String text, int y, int y); // String starting at x,y,
current color & font
public void clearRect(int x, int y, int width, int height); //
clear given rectangular area
publicvoid fillRect(int x, int y, int
width, int height);
// fill rectangle area w. current color
public void fillOval(int x, int y, int width, int height);
public void setColor(Color color); // set current color
public void setFont(Font font); // set current font
Typically, you use drawing inside of containers,
because they offer a blank background that can be used as a drawing area. You
can use drawing shapes to define new widgets, if you want, e.g. elliptical
buttons – then you will need to trace mouse coordinates in order to find out
whether your component was clicked.
In order to make permanent drawings, you have to override the container’s paint() method, which is called by the VM whenever a
redrawing is required. The VM passes the container’s current Graphics object as
a parameter, and you apply your drawing methods to this object. You cannot call
paint() yourself, but you can call the parameterless method repaint() which triggers the VM to call paint()in order
to make your drawings visible.
public void paint(Graphics
g);
In Scribble,
however, we need to draw as reaction to runtime events. Overwriting paint() does not
work in this case, because we would have to know the shapes to be drawn at
programming time. Rather, we need to do the drawing in the event handling
methods. But where do they get a Graphics object from? Every component defines
a method
getGraphics()
which yields the currently used copy of the Graphics
object. Use that one for drawing as a response to mouse events. The drawback of
this Graphics object is that it is of limited validity: It is discarded with
the next call of paint().This
means that calling repaint() can be used to clear the drawing area…
Careful: getGraphics() yields null until the component is displayed for the
first time.
Here is a little
list of typical traps when working with Graphics:
1.
(0,0) denotes the top
left corner of the component (not of the surrounding main window).
2.
All drawing shapes are positioned using their
top left corner. But for text, the bottom left corner is used.
3.
If the coordinates of a drawing lie outside the
drawing area, it is invisible (no error message). So for text, always use a y
value of at least 5.
4.
Use repaint carefully: It erases all
non-permanent drawings, i.e. everything that is not programmed in paint().
5.
If you use getGraphics(), catch NullPointerExceptions
because they are not unlikely to occur.
Scribble consists of
a drawing area with two widgets: a clear button, and a combo box for choosing
drawing colors. Lines dragged with the mouse are
displayed in the chosen color.
1.
How many active objects (threads) are needed?
2.
Are you going to call drawLine
in the paint method or elsewhere?
3.
Will your drawings survive repaint calls?
4.
How will your drawings react to window resizing?
Will they grow and shrink accordingly?
5.
Do you expect the opening of the combo box to
cause any problems?
6.
How
will you implement the clear button handler, i.e. what does the actionPerformed method do?
Write
a program that draws a Japanese flag, i.e. e white rectangle with a red circle
in it:
1. Extend JFrame
and set its size in the constructor.
2. Override paint, drawing a white
rectangle and a red circle.
3. In the main method, instantiate your
class.
Scribble Program Setup:
Define
two classes:
1. ScribbleFrame is a JFrame
and contains exactly one JPanel in ist center:
ScribblePanel.
2. ScribblePanel is a Jpanel
and implements MouseListener and MouseMotionListener.
Drawing:
1.
drawLine has 4 Parameters, 2 sets of
coordinates. Define 4 int attributes to store the
corresponding mouse coordinates.
2.
Now write the event handler methods MousePressed() and MouseDragged().Use MousePressed() to store the coordinates as the start
coordinates of a line, and MouseDragged() to draw
a line and update the start coordinates. Remember that you can get a Graphics
object using the getGraphics method
of your container – in this case ScribblePanel.
3.
What about the other handler methods?
4.
Test your first scribbler. You can erase your
drawing by resizing your window.
.
Erase
button:
1.
Implement
and test an erase Button, using an anonymous Handler. Calling repaint()will erase your drawings.
2.
Remember
to either change the Layout or add the button to the NORTH part of your panel.
Color choice:
1.
Implement
and test a color combo box. Add a new attribute currentColor to your ScribblePanel.
2.
As
the user cannot change the color in the middle of a
line, check the combo box in
MousePressed() and call setColor()
to define the line color.
3.
Test
your Color choice.
4.
Which
color is used after erasing? Is that the expected behaviour? If not,
modify your program accordingly.
You should get this far in the lab.
Now
take this result and integrate it as a
further tab into your "Divelog" application
from the weekend workshop. Feel free to invent a fanciful title for this tab, e.g.
"subaqua art"...
Extra work:
ComboBox without erasing:
To avoid erasing caused by an opened combo box,
move the combo box to the bottom of the window.
ColorChooser:
1.
Replace
the combo box by a button labelled "Colors".
2.
Write
an anonymous handler for it which shows a JColorChooser
dialog and stores the result in a color attribute.
Read this attribute instead of the combo box state in MousePressed().
3. Ersetzen Sie die ComboBox durch eine JButton mit der Aufschrift "Farbwahl".
4. Schreiben Sie einen anonymen Ereignisbehandler für den Farbwahl-Knopf, der ein JColorChooser-Fenster anzeigt und das Ergebnis in das Feld aktuelleFarbe einträgt.
Permanent Drawings (difficult):
1.
Instead
of drawing, store the color and the mouse positions
of each line in a collection (of type Vector). Use a second Vector to store all
lines.
1.
Override
paint: let it take the information from the lines Vector and draws the lines.
2.
Re-implement
the clear button: let it empty the lines Vector.
© Ilse Schmiedecke 2005 – for questions and remarks schmiedecke@tfh-berlin.de