We will now look at some of the basic graphics (drawing and lettering). Consider the appropriate widget for drawing:
An AWT programmer would try to "paint" on a JFrame. Important: Don't override paint() unless you know what you are doing! Instead, in Swing, we usually draw on a JPanel. Turns out, you can draw on most Swing components, but are not advised to draw on top-level components like JFrame.
All drawing occurs on a graphics context: A graphics context is an instance of class Graphics. However, you cannot instantiate this class yourself. A component (like JPanel) does that for you. Thus, to draw on a JPanel, you need to get a Graphics instance from the frame. The way this works is, you get the Graphics instance via paintComponent(Graphics g)
The all-important technique of overriding paintComponent() (and, sometimes, repaint()):
Like AWT, Swing does not maintain "bit-mapped memory". That is, if a window is covered and later uncovered, Swing does not re-draw the contents. Thus, when re-drawing is needed, you have to do it. Swing tells you when drawing is needed by calling the paintComponent() method. Thus, you need to override the paintComponent() method to "do your thing". Important: Therefore, you need to extend the appropriate class (usually JPanel to override the paintComponent() method. This method is given a Graphics instance as parameter.
import java.awt.*; import javax.swing.*; // Extend JPanel to override its paintComponent() method: class NewPanel extends JPanel { // Set background in constructor. public NewPanel () { this.setBackground (Color.cyan); } // Override paintComponent(): public void paintComponent (Graphics g) { // Always call super.paintComponent (g): super.paintComponent(g); // drawString() is a Graphics method. // Draw the string "Hello World" at location 100,100 g.drawString ("Hello World!", 100, 100); // Let's find out when paintComponent() is called. System.out.println ("Inside paintComponent"); } } public class TestSwing4 { public static void main (String[] argv) { // Create a frame JFrame f = new JFrame (); // Set the title and other parameters. f.setTitle ("Hello World Test"); f.setResizable (true); // Background is going to be Panel's background. // f.getContentPane().setBackground (Color.cyan); f.setSize (500, 300); // Add the panel using the default BorderLayout NewPanel panel = new NewPanel (); f.getContentPane().add (panel); // Show the frame. f.setVisible (true); } }
Try it and see the result:
An explanation: To write or draw in a frame, we need to override the paintComponent() method in JPanel.
To override means we have to inherit from JPanel.
Above, the class NewPanel extends JPanel: class NewPanel extends JPanel // ...
Note the unusual syntax in replacing Container cPane = f.getContentPane (); cPane.add (panel);
with the variable-free f.getContentPane().add (panel);
If you are uncomfortable with this form, use the former syntax. Since the NewPanel component will fill up the contentPane, it's background will be visible. Therefore, we set the Panel's background color to what we want. Note that paintComponent() is passed a Graphics context. Only a graphics context has the means for drawing and writing. Here are some methods in Graphics, for example:
In the lab you drew some geometric figures: We will draw un-filled and filled versions of a square, a circle and a rectangle. All the drawing methods are in Graphics. We will use methods drawRect(), drawOval() and drawRoundRect(). The ideas are the same in Swing.
import java.awt.*; import javax.swing.*; // Extend JPanel to override paintComponent () class NewPanel extends JPanel { public NewPanel () { this.setBackground (Color.cyan); } public void paintComponent (Graphics g) { super.paintComponent (g); // Draw a Square: g.drawRect (50,50,50,50); g.drawString ("Square", 50, 115); // Circle: g.drawOval (200,50,50,50); g.drawString ("Circle", 200, 115); // Rounded rectangle: g.drawRoundRect (350,50,75,50,20,20); g.drawString ("Rectangle", 350, 115); // Draw a line across the middle: g.drawLine (0,150,500,150); // Now draw some filled shapes: // Square: g.fillRect (50,200,50,50); g.drawString ("Square", 50, 265); // Circle: g.fillOval (200,200,50,50); g.drawString ("Circle", 200, 265); // Rounded rectangle: g.fillRoundRect (350,200,75,50,20,20); g.drawString ("Rectangle", 350, 265); } } // Create a new Frame class. class NewFrame extends JFrame { public NewFrame () { // Set the title and other parameters. this.setTitle ("Some Geometric Figures"); this.setResizable (true); this.setSize (500, 300); // Create and add the panel. NewPanel panel = new NewPanel (); this.getContentPane().add (panel); // Show the frame. this.setVisible (true); } } public class TestSwing6 { public static void main (String[] argv) { // Simply fire up the whole application. NewFrame f = new NewFrame (); } }
Complete the paintComponent() method in the following template to draw the "Stars" part of the Lewis and Clark flag. No need for real stars (unless you feel like it... no penalty for extra effort): Just use filled circles.
import java.awt.*; import java.awt.event.*; import javax.swing.*; class NewPanel extends JPanel { public NewPanel() { setBackground(Color.blue); } public void paintComponent(Graphics g) { super.paintComponent(g); int width = getWidth(); int height = getHeight(); // Your code goes here } } class NewJFrame extends JFrame { // Constructors. public NewJFrame (int width, int height) { // Set the title and other parameters. this.setTitle ("Fifteen stars"); this.setResizable (true); this.setSize (width, height); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); NewPanel panel = new NewPanel(); getContentPane().add(panel); // Show the frame. this.setVisible (true); } // No-parameter constructor - use a default size. public NewJFrame () { this (500, 300); } } public class Flag15 { public static void main (String[] argv) { NewJFrame nf = new NewJFrame (500, 300); } }