View Basics: JFrames and JPanels

Advanced Programming/Practicum
15-200


Introduction In this lecture we will begin exploring the View part of the Model-View-Controller (MVC) pattern for writing GUI applications. We will primarily discuss the JFrame and JPanel classes: two critical classes (defined in the javax.swing package) that programmers extend when writing GUIs. We will write these classes independently here, but sometimes using inner classes simplifies their code.

Basically, a JFrame represents a framed window and a JPanel represents some area in which controls (e.g., buttons, checkboxes, and textfields) and visuals (e.g., figures, pictures, and even text) can appear. Windows can contain/display multiple panels, although in the simplest GUIs, we will associate just one panel with a window.

When we discuss JFrames and JPanels, we will discuss just a very few of their many methods: they are highly subclassed (in a deep hierarchy) and therefore inherit many (many) interesting methods. We will discuss only the most useful and important of these methods: the ones used in most GUI applications. When reading this lecture (and programming using these classes) you should always have a Javadoc browser open, and not be afraid to search it for some useful method.

In the process of discussing JFrame and JPanels,, we will also discuss a host of other useful (and simpler) classes: Toolkit, Image, Dimension, Point, Color, Font, FontMetrics, and Graphics. These classes are imported and then used in many GUI applications. We use the last, Graphics, to render visuals (e.g., figures, pictures, and even text) in a JPanel that contains the graphics part of a JFrame.

Students at this point in the course are expected to be able to read the Javadoc of these classes (in Sun's API) to learn about their structure and use and to explore various methods (whose names indicate they might be useful). Note that some of these classes declare static methods and fields (mostly constants), which are easily spotted because their uses in our programs are prefaced by the name of the class (whose first letter is capitalized).

We will also begin to discuss the concept of listeners and adapters, which we will use much more extensively when we discuss the Controller part of the MVC pattern. Finally, we will examine a bit of control flow in GUIs: how methods are called: in Java they can be called explicitly as well as implicitly.

There is a folder that contains various projects associated with this lecture. Please download View Demonstrations and unzip this folder and examine its contents briefly now (we will refer to details later).


The JFrame Class For most GUIs implemented via the MVC pattern, we write their View class by subclassing the JFrame class (which is defined in the javax.swing package). Swing is a collection of related classes for writing stand-alone Java applications: applets are written using other Java packages. There are entire books written on how to use Swing.

By extending JFrame, the View subclass inherits hundreds of methods from not only JFrame, but its superclasses: the JFrame class is in a hierarchy whose superclasses include (going upward) Frame, Window, Container, Component, and finally Object).

At the top of this heirarchy is the Component class, which is abstract; objects from its concrete subclasses are displayable on the screen, and have the following important methods (along with many many more): isVisible/setVisible (with a boolean parameter), getSize/setSize (with either a Dimension or two int parameters), getLocation/setLocation (with either a Point or two int parameters), and repaint (parameterless). The Container class is itself considered a Component, but one that in addition collects together other Components, using a LayoutManager (discussed in the next lecture) to organize them visually; it takes the role of a composite pattern: when we perform an operation on a Container, it performs that operation on all its Components. The Window class represents a raw window (e.g., no border) with its own internal coordinate system: (0,0) is the windows upper-left hand corner. The Frame class represents a window with Border and title area (we can specify various kinds of borders and set its associated icon/title). Finally, the JFrame class represents a Java compatible window: it has an associated content pane, which shows its visual contents.

Typically we define a parameterless constructor and one new method named build, which calls many of these inherited methods to initialize the JFrame's window; this method is called in the main method in Application (after constructing the model, view, and controller). For the simplest GUIs, this is not strictly necessary: we can put all this code in the constructor for the class itself; but for more complicated GUIs, separating construction from building is better.

A JFrame appears as a standard window; the one we will use in the earlist part of this lecture displays as follows.

 

Every pixel (picture element) on a computer screen is represented by a color, which is a combination of how much red, green, and blue appear there. The upper-left hand corner of the screen has an x coordinate of 0 and a y coordinate 0; thus x coordinates increase from left to right, and y coordinates increase from top to bottom (unlike our standard coordinate system). The bottom-right hand corner has the largest x and y coordinates (this number varies, depending on the size of the screen): a representative high-resolution value is 1280 horizontal by 1024 vertical pixels (for a total of 1,310,720 pixels).

Each JFrame (because it is a subclass of Component) has a location on the screen (for its upper-left hand corner) and size. Two simple JFrame methods (inherited from Component) are getLocation which returns a Point object (which has public instance variables x and y) and getSize which returns a Dimension object (which has public instance variables height and width). Likewise, it has setLocation and setSize methods, which use objects from these classes as parameters, or just two int parameters.

Each JFrame (because it is a subclass of Frame), has a header that contains (going left to right) an icon (little picture; it is optional), a title (text), and way to the right three window control buttons(minimize, midimize/maximize, and terminate). We can change the appearance of the icon by using the method setIconImage; we can change the title using the method setTitle. Likewise there are getIconImage and getTitle methods.

To read an icon from a file and put it into the header first requires calling the getImage method in the Toolkit class, passing its result to the setIconImage method. The getImage method can be called with a String parameter that names the file that stores the icon (in .gif, .jpeg, .png format). It returns a reference to an Image of that icon (or null if the file cannot be found, or it does not store information of the required type). Here is the standard code to perform all these operations; the methods are called inside the View class:

  Toolkit tk        = Toolkit.getDefaultToolkit();
  Image   titleIcon = tk.getImage(gifFileName);
  setIconImage(titleIcon);
Notice how a Toolkit object is gotten: via the static getDefaultToolkit method from the Toolkit class, not by construction. Technically, even this short code can be simplified to
  Toolkit tk        = Toolkit.getDefaultToolkit();
  setIconImage(tk.getImage(gifFileName));

We cannot change the appearance of the window control buttons, but we can change the behavior of each (using the method addWindowListener, which we discuss below). This method is inherited from the Window superclass.

The JFrame stores a reference to its content pane. The JFrame class itself defines getContentPane and setContentPane methods; the content pane itself is a Container. When we learn about the JPanel class we will explore this connection further. In the simplest the content pane is just one panel; but, we can use the add method (inherited in Container) to add many Components into the content pane of a JFrame; Java uses a layout manager (it is told which one to use via setLayoutManager) to determine the how these components are arranged Layout managers are the topic of the next lecture. We will also see that every JPanel has its own coordinate system, with (0,0) as the upper-left hand corner, which greatly simplifies programming it.

Finally, the method setVisible (with a boolean parameter) determines whether a JFrame appears on (or disappears from) the screen.


Adapters An adapter is a kind of pattern. Typically, it is a concrete class with a variety of stub methods (typically void methods that immediately return) that can be overriden. For example, the WindowAdapter class includes the following methods (see Javadoc for more details), which are called when the user clicks a window control button: WindowActivated, WindowClosed, WindowDeactivated, WindowClosing, WindowIconified, WindowDeiconified, and WindowOpened. In the WindowAdapter class, these methods just return immediately, without doing anything (but these methods exist, and are called when the user clicks a window control button).

The JFrame class inherits (from the Window class) the addWindowListener method from the Window superclass. It "listens" for certain components being clicked and call the appropriate method for each component. Its parameter must be a reference to an object constructed from a class that implements an interface named WindowListener. In fact, the WindowAdapter class implements this interface by defining each of its methods, as a stubs. Thus we can write in a JFrame method

  addWindowListener(new WindowAdapter());
Of course, because every method in this class does nothing, we have added no new interesting behavior to the JFrame with this method call. But now, let's define some interesting behavior when the window is closed. We can define the following class.
  public class Terminator extends WindowAdapter {
    public void windowClosing(WindowEvent e)
    {
      System.out.println("Window closing selected: terminating program");
      System.exit(0);
    }
  }
Java supplies a default constructor for the Terminator class. This class inherits all the stub methods from the WindowAdapter superclass, but it overrides the windowClosing method (the code inside says that when the user clicks the terminate button on this window, print a message, and the entire program itself that created the window should terminate). Note that this class implements the WindowListener interface: we can specifically say it does, or as illlustrated above, not say it: Java knows that any subclass (of a class that implements an interface) also implements that interface, because at worst it just inherits all of the needed methods. Given this class, we can write
  addWindowListener(new Terminator());
to give the JFrame the new behavior that we want.

Notice that we are defining this subclass just to construct one instance of it, to pass to the addWindowListener method. This is exactly what anonymous classes are useful for: to supply a more compact (but complex) syntax for constructing one object from a class that is never needed again. Recall that it involves constructing an object from an anonymous (nameless) class. The syntax in this case is

  addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e)
    {
      System.out.println("Window closing selected: terminating program");
      System.exit(0);
    }
  });
Here we say new WindowAdapter() but immediately follow it by a block that overrides only the windowClosing method from that class (we could override any number of these methods). It says to Java, construct an object from an anonymous subclass whose superclass is WindowAdapter; the anonymous subclass extends WindowAdapter, inheriting all its methods and overriding only the windowClosing method.

Note that we have discussed two different concepts here.

  • The concept of an adapter class (which implements an interface with stub methods). We can use such classes directly or more likely construct subclasses of them (and then use objects constructed from these subclasses instead). The key to an adapter class is that it already implements an interface, with default method meanings; we can easily create a subclass from it, overriding a method or two, to specify different behavior for some action(s).

  • The concept of an anonymous class (based on an adapter class). When we need to construct just one object from a subclass, we don't really even need to name the subclass. Instead, we can use special Java syntax to construct an object from a subclass of a named superclass by specifying the superclass name and the overridden methods.
We will see adapter classes used frequently for other kinds of listeners (buttons, mouse, keyboard) when we study the Controller part of the MVC pattern. It is a general technique that we can use in other places in Java programs too.

Sample JFrame Programs Examine the folder named basic jframe in the View Demonstrations download. It shows a short program (small View class and tiny Application class) that illustrates how to use javax.swing.JFrame. The console controls this window appearing and disappearing. The JFrame's size is always at 1/2 the width/height of the screen. Its header includes an icon and title (which changes slightly, via a counter/location, each time the window appears. The window's location changes each time it disappears. When the window is closed, the entire program terminates (including the loop in main).

You can experiment with the methods called to attempt to get other forms of interesting behavior, including special behavior for other methods specified by the WindowAdapter.


The JPanel Class Once we write a JFrame for our GUI, we often add one or more objects to it, each from a subclass extending JPanel. Typically, each sublcass will define some instance variables and a constructor (to initialize them), and overrides the inherited paintComponent method with one that is special to the subclass. The paintComponent method determines what figures, images, and/or text (controls like buttons and textfields are painted automatically) appear on the part of the screen that is owned by the JPanel.

To override the inherited paintComponent method, we must write a void method with one parameter: a reference to an object of type Graphics (called the Graphics context: see the next section for a short discussion of this class), which contains all the methods for drawing figures, images, and text (see its Javadoc for details). The Java runtime system automatically calls this paintComponent method whenever the size of its JFrame is changed, whenever other windows are moved to uncover part of the JFrame, and whenever the JFrame is deiconified after being iconified (subclasses of the WindowAdapter can add special behavior for such events as well).

In all cases, the paintComponent method is called to redisplay the contents of the JPanel, now that the amount of it that is visible has been "changed". The Java runtime automatically supplies the paintComponent method with the right Graphics context (we never have to worry about that).

If we want to explicitly call the paintComponent method, say call it repeatedly to force Java to animate some actions in a JPanel, we cannot call it directly, because we cannot access its Graphics context. Instead, we must call the void, parameterless method repaint (either on a specific JPanel or on its JFrame, which automatically calls it on each JPanel in its content pane).

The biggest conceptual problem that students have about GUIs is that they THINK the screen stores all the information written to it. When a JPanel paints itself, all the figures, images, and text are written into the screen: some are visible, some are not (because they go beyond the window's boundary, or is covered by another window). The hidden information is GONE. If the JPanel needs to redisplay any part of itself, it can do so only by calling paintComponent again (which typically displays it all). For sophisticated applications, overloaded versions of the paintComponent method might be able to determine what part of its image to repaint, but in simple applications, it just repaints everything.

So, if we need to display a bunch of objects in a JPanel, we must store information about each of these objects (say in a collection class), and then the paintComponent method should iterate through this collection, displaying each object in the JPanel


JPanel, paintComponent, and the Graphics Context Whenever the paintComponent method of a JPanel is called (whether by the Java system or the program itself), it is supplied with a reference to an object from the Graphics class (called its graphics context). The graphics context stores state, including where on the screen the upper-left hand corner of the JPanel is, what is the current color for drawing/text, etc. It does not store anything drawn in the JPanel. The paintComponent method general uses methods from the Graphics class to draw lines, rectangles, ovals, images, and text. In the process, it can change some of these states. In the next few sections, we will describe how to display figures, images, text, and buttons in such a panel.

Note that every JPanel objects inherits from Component the getBackground and setBackground methods, which has a Color as return value/parameter respectively. Finally, we should always call super.paintComponent() before doing anything graphical in the paintComponent method.


Drawing Figures In this section we will examine some simple methods for drawing figures in a graphics context. First of all, whenever we specify coordinates, the graphics context will translate them so that (0,0) is the upper-left hand corner of the JPanel. Parts of drawings may go beyond the screen. This is not an error: such parts just won't be seen. Whenever we specify the x and y coordinates of a figure, they refer to the upper-left hand of the figure (NOT ITS CENTER). Here are prototypes for the simplest drawing methods in the Graphics class.
  void drawArc(int x, int y, int width, int height,
              int startAngle, int arcAngle) 
  void drawLine(int x1, int y1, int x2, int y2) 
  void drawOval(int x, int y, int width, int height) 

  void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) 
  void drawPolygon(Polygon p) 
  void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) 
  void drawRect(int x, int y, int width, int height) 
  void drawRoundRect(int x, int y, int width, int height,
                     int arcWidth, int arcHeight)
Line are straight lines; ovals are drawn with the specified major and minor axes (circles have these axes the same); a polygon is specified either by either two parallel arrays of matching x and y coordinates, or a Polygon object (see Javadoc) which stores such arrays internally (and can determine whether a point is in a polygon); a polyline is like a polygon, but its last point is not connected to its first; rectangles come in two flavors: square corners and rounded corners (squares just have their width and height the same.

Likewise, there are simlar methods that draw filled-in shapes.

  void fillArc(int x, int y, int width, int height,
               int startAngle, int arcAngle) 
  void fillOval(int x, int y, int width, int height) 
  void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) 
  void fillPolygon(Polygon p) 
  void fillRect(int x, int y, int width, int height) 
  void fillRoundRect(int x, int y, int width, int height,
                     int arcWidth, int arcHeight)
A large variety of figures can be drawn with combinations of these methods. Note that we can call the getBackground method of a Component; by drawing something in this color (using the setColor method), Java will seem to erase what it underneath it.

Examine the folder named figures jpanel in the View Demonstrations download. It shows a short program (small DrawPanel class, tiny View and Application class) that illustrates how to use javax.swing.JPanel for drawing figures in a JFrame.


Drawing Images In this section we will examine two simple methods for drawing images in a graphics context. We have already seen how to read images stored in files with a Toolkit. We will now see how to render such images directly and how to render them after scaling them. Here are prototypes for the simplest image methods in the Graphics class.
 boolean drawImage(Image img, int x, int y,
                     ImageObserver observer)
  boolean drawImage(Image img, int x, int y,
                    int width, int height, ImageObserver observer)
The x and y values specify the upper-left hand of the image. The first method renders the image in it actual size; the second shrinks or expands it to the dimensions width and height. For now, we will specify null for the ImageObserver

Examine the folder named images jpanel in the View Demonstrations download. It shows a short program (small DrawPanel class, tiny View and Application class) that illustrates how to use javax.swing.JPanel for drawing images. It draws a bunch of hot dogs, and moves them a bit each time enter is pressed in the console window.


Drawing Text In this section we will examine some simple methods for drawing text in a graphics context. The process is straightforward, but it can involve two other interesting classes, Font and FontMetrics.

Objects in the Font class are constructed, specified by a font name (e.g., SanSerif), style (e.g., plain, bold, italic), and size (e.g., 12 point: specified in points, which is 1/72 of an inch). Objects in the FontMetrics class describe a font, with lots of interesting information: the height and width of characters in the font, ascent and descent, etc.

Here are prototypes for the simplest text methods in the Graphics class.

  void drawString(String str, int x, int y) 
  Font getFont() 
  FontMetrics getFontMetrics() 
  FontMetrics getFontMetrics(Font f) 
  void setFont(Font font)
Note: the x and y values specify the baseline of the the text, not its upper-left hand (the baseline is the bottom of the character, not including the descender). The first method renders the text (using the current font and color) starting at the specified coordinate. Before this statement we might write the following code to specify the font to use and get its metrics.
  Font        f1  = new Font("SansSerif", Font.BOLD, 14);
  FontMetrics f1m = g.getFontMetrics(f1);
  g.setFont(f1);
  g.setColor(Color.blue);
With any FontMetrics object we can call the stringWidth method with any String: it returns the width of that String using that Font

Examine the folder named text jpanel in the View Demonstrations download. It shows a short program (small DrawPanel class, tiny View and Application class) that illustrates how to use javax.swing.JPanel for drawing text. It also illustrates some uses of the Font and FontMetrics classes, which are both defined in the java.awt package.


Buttons Finally, in this section we will examine how to put buttons in JPanels. Here we use Buttons only for their visual effect; in the lecture on Controllers we will learn how to make pressed buttons call appropriate methods in the Model.

Standard buttons are constructed from the JButton class (in the javax.swing package). As with other controls, they inherit hundreds of methods. The typical constructor specifies a String to use as a label for the button (or we can call setLabel with any String later). We can also call inherited methods such as setFont, setBackground, and setForeground, although most standard buttons use default values for these. We can also call the setEnabled method (with a boolean parameter) to tell Java whether a button is pressable (if not, its label will appear as a faded color).

Here is an example of declaring a JButton and performing many of these operations.

  JButton b = new JButton("Cold");
  b.setFont(new Font("Monospaced",Font.BOLD,12));
  b.setBackground(Color.yellow);
  b.setForeground(Color.blue);
Finally, we can use the add method to add any kind of Component to a JPanel. In the program below, we first construct a JPanel and store its value in a local variable, and then call add multiple times, once to put each button in the panel. Recall that a special layout manager (which we will cover in the next lecture) determines where each button will go.

Examine the folder named button jpanel in the View Demonstrations download. It shows a short program (tiny DrawPanel class, small View class and tiny Application class) that illustrates how to use javax.swing.JPanel for drawing buttons.


Problem Set To ensure that you understand all the material in this lecture, please solve the the announced problems after you read the lecture.

If you get stumped on any problem, go back and read the relevant part of the lecture. If you still have questions, please get help from the Instructor, a CA, or any other student.

  1. None yet.