Panels


A JFrame envelopes a content panea place where you put visual content. A content pane object is a container of type java.awt.Container. You add other components to a container, including things such as list boxes, text, and containers themselves. In Swing, the workhorse container class is JPanel. Like JFrame, the JPanel class is a view class, used only for display purposes.

You may have noticed the package and naming convention. Swing components all begin with the letter J (JFrame, JPanel, JList, and so on); their containing package is javax.swing or a subpackage of javax.swing. The package java.awt (or a subpackage thereof) contains AWT classes. AWT class names do not use a prefix.

Your first piece of relevant user interface will be to display the text "Courses:" to the end user. You could directly embed this text in the JFrame's content pane. A better approach is to build up a second container consisting of the text, then embed this container in the content pane. To do so, you will create a JPanel subclass that displays text to the end user. You will then embed this JPanel in the JFrame's content pane.

Here is the spike for the JPanel:

 package sis.ui; import javax.swing.*; import java.awt.*; public class CoursesPanel extends JPanel {    public static void main(String[] args) {       show(new CoursesPanel());    }    private static void show(JPanel panel) {       JFrame frame = new JFrame();       frame.setSize(100, 100);       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       frame.getContentPane().add(panel); // 1       frame.setVisible(true);    }    public CoursesPanel() {       JLabel label = new JLabel("Courses:");       add(label);    } } 

The spike uses a main method containing a bit of "driver" code to allow you to manually test the panel. If you were to code a second panel class, you would be compelled to refactor this driver code into a common utility class.

Within the constructor of CoursesPanel, you create a new JLabel object. A JLabel is a widget that displays text to an end user. You specify the text via the constructor of JLabel. In order for the JLabel to be displayed, you must add it to the JPanel using the add method.

CoursesPanel does not define add, nor does the JPanel class from which CoursesPanel extends. You must traverse the inheritance hierarchy up to the class java.awt.Container in order to find the definition for add. It makes sense for add to belong there: You add other components, including JPanel objects, to Container objects.

Following is the inheritance hierarchy for CoursesPanel.

 Object  |  |java.awt.Component      |      |java.awt.Container          |          |javax.swing.JComponent              |              |javax.swing.JPanel                  |                  |sis.ui.CoursesPanel 

Everything in Swing sits atop the AWT framework. Everything is a Component. All Swing components (JComponent objects) are also containers. JPanel and CoursesPanel are therefore containers.

In order to display a panel, you can add it to the content pane of a JFrame. The line marked 1 in the show method of CoursesPanel accomplishes this.

Compile and execute the CoursesPanel spike. The result should look like Figure 2.

Figure 2. A JPanel


What should a test for CoursesPanel prove? For now, the only significant thing that CoursesPanel does is to present a label to the user. The test should thus ensure that the panel contains a label with the correct text:

 package sis.ui; import junit.framework.*; import javax.swing.*; import java.awt.*; public class CoursesPanelTest extends TestCase {    public void testCreate() {       CoursesPanel panel = new CoursesPanel();       JLabel label = getLabel(panel);       assertEquals(CoursesPanel.LABEL_TEXT, label.getText());    }    private JLabel getLabel(JPanel panel) {       for (Component component: panel.getComponents())          if (component instanceof JLabel)             return (JLabel)component;       return null;    } } 

The job of the method getLabel is to take as its parameter a JPanel and return the first JLabel object encountered within the JPanel. Since a JPanel is a Container, you can ask it for the list of components it contains using the getComponents message. Each element contained within the list is of type java.awt.Component. You must test the type of each element using instanceof to determine if it is a JLabel. You must also cast the matching element in order to return it as a JLabel type.

The use of instanceof suggests that perhaps a better approach works to determine if a component exists. For now, the technique will suffice until you have to test for a second type of componentor a second label.

Is this test necessary? The answer is debatable. You will always manually execute a Swing application from time to time to take a look, even if you were to write thousands of automated tests. In doing so, you will quickly determine whether or not a specific component appears.

One could also ask the opposite question: Is this test sufficient? Not only could you test that the widget appeared in the window but you could also test that it was presented in the appropriate font and appeared at the correct coordinates.

My view is that the test may be mild overkill, but it's not a difficult test to write. I would rather expend the effort than find out the hard way that someone accidentally changed the JLabel text in one of 200 screens. Positioning-based layout tests (tests that prove where the label appears onscreen) are another matter. They are notoriously difficult to write and even more difficult to maintain. A bit of aesthetic distress is not usually a barrier to successful application execution.

More important is that you write tests for any dynamic user interface capabilities. For example, you might allow the application to change the color of text when the user presses a button. You will want to write a test to ensure that this action and reaction works as you expect.

The CoursesPanel class differs from the spike only in that it defines a class constant representing the label text.

 package sis.ui; import javax.swing.*; import java.awt.*; public class CoursesPanel extends JPanel {    static final String LABEL_TEXT = "Courses";    ...    public CoursesPanel() {       JLabel label = new JLabel(LABEL_TEXT);       add(label);    } } 

In the Sis application, you want CoursesPanel to represent the main view. The modified SisTest uses a similar mechanism to that in CoursesPanelTest to prove that the content pane contains a CoursesPanel instance.

 package sis.ui; import junit.framework.*; import javax.swing.*; import java.awt.*; public class SisTest extends TestCase {    ...    public void testCreate() {       final double tolerance = 0.05;       assertEquals(Sis.HEIGHT, frame.getSize().getHeight(), tolerance);       assertEquals(Sis.WIDTH, frame.getSize().getWidth(), tolerance);       assertEquals(JFrame.EXIT_ON_CLOSE,                    frame.getDefaultCloseOperation());       assertTrue(containsCoursesPanel(frame));    }    private boolean containsCoursesPanel(JFrame frame) {       Container pane = frame.getContentPane();       for (Component component: pane.getComponents())          if (component instanceof CoursesPanel)             return true;       return false;    }    ... } 

You're already familiar with how to get a frame to include and show a panel. You wrote virtually the same code in the "driver" for CoursesPanel.

 package sis.ui; import javax.swing.*; public class Sis {    ...     Sis() {       frame.setSize(WIDTH, HEIGHT);       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       frame.getContentPane().add(new CoursesPanel());    }    ... } 

The tests pass.



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net