Miscellaneous Aesthetics


In this section you'll learn how to enhance the look of the existing Courses-Panel.

JScrollPane

If you add more than a few courses using sis.ui.Sis, you'll note that the JList shows only some of them. To fix this problem, you can wrap the JList in a scroll pane. The scroll pane acts as a viewport onto the list. When the list's model contains more information than can be displayed given the current JList size, the scroll pane draws scroll bars. The scroll bars allow you to move the viewport either horizontally or vertically to reveal hidden information.

The bold code in this listing for the CoursesPanel adds a scroll pane. You can specify whether you want to show scroll bars always or only as needed using either setVerticalScrollBarPolicy or setHorizontalScrollBarPolicy. I find it more aesthetically pleasing to always show the vertical scroll bar.

 private void createLayout() {    JLabel coursesLabel =       createLabel(COURSES_LABEL_NAME, COURSES_LABEL_TEXT);    JList coursesList = createList(COURSES_LIST_NAME, coursesModel);    JScrollPane coursesScroll = new JScrollPane(coursesList);    coursesScroll.setVerticalScrollBarPolicy(       ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);    setLayout(new BorderLayout());    add(coursesLabel, BorderLayout.NORTH);    add(coursesScroll, BorderLayout.CENTER);    add(createBottomPanel(), BorderLayout.SOUTH); } 

Borders

The courses list and associated labels directly abut the edge of the panel. You can use a border to create a buffer area between the edges of the panel and any of its components.

 private void createLayout() {    JLabel coursesLabel =       createLabel(COURSES_LABEL_NAME, COURSES_LABEL_TEXT);    JList coursesList = createList(COURSES_LIST_NAME, coursesModel);    JScrollPane coursesScroll = new JScrollPane(coursesList);    coursesScroll.setVerticalScrollBarPolicy(       ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);    setLayout(new BorderLayout());    final int pad = 6;    setBorder(BorderFactory.createEmptyBorder(pad, pad, pad, pad));    add(coursesLabel, BorderLayout.NORTH);    add(coursesScroll, BorderLayout.CENTER);    add(createBottomPanel(), BorderLayout.SOUTH); } 

You can use the BorderFactory class to create one of several different border types. Most of the borders are for decorative purposes; the empty border is the exception. You can also combine borders using createCompoundBorder. The reworked createLayout method demonstrates the use of a few different border types.

 private void createLayout() {    JList coursesList = createList(COURSES_LIST_NAME, coursesModel);    JScrollPane coursesScroll = new JScrollPane(coursesList);    coursesScroll.setVerticalScrollBarPolicy(       ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);    setLayout(new BorderLayout());    final int pad = 6;    Border emptyBorder =        BorderFactory.createEmptyBorder(pad, pad, pad, pad);    Border bevelBorder =        BorderFactory.createBevelBorder(BevelBorder.RAISED);    Border titledBorder =        BorderFactory.createTitledBorder(bevelBorder, COURSES_LABEL_TEXT);    setBorder(BorderFactory.createCompoundBorder(emptyBorder,                                                 titledBorder));    add(coursesScroll, BorderLayout.CENTER);    add(createBottomPanel(), BorderLayout.SOUTH); } 

The use of a titled border eliminates the need for a separate JLabel to represent the "Courses:" text. The elimination of the JLabel will break testCreate in CoursesPanelTestdid you remember to make this change by testing first?

Adding a Title

No text appears in the SIS frame window's titlebar. Rectify this by updating testCreate in SisTest:

 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());    assertNotNull(Util.getComponent(frame, CoursesPanel.NAME));    assertEquals(Sis.COURSES_TITLE, frame.getTitle()); } 

JFrame supplies constructors that allow you to pass in titlebar text. The code in Sis:

 public class Sis {    ...    static final String COURSES_TITLE = "Course Listing";    private JFrame frame = new JFrame(COURSES_TITLE);    ... } 

Icons

The final aesthetic element you'll add is an icon for the window. By default, you get the cup-o-Java icon, which appears as a mini-icon in the titlebar and when you minimize the window. Since the icon is part of the titlebar, it falls under the control of the frame window.

A test can simply ask for the icon from a frame by sending it the message getIconImage. The method, implemented in java.awt.Frame, returns an object of type java.awt.Image. The code in testCreate asserts that the icon retrieved from the SIS frame is the same as one explicitly loaded by name. Both the test and the sis.ui.Sis code will use a common utility method to load the image: ImageUtil.create.

 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());    assertNotNull(Util.getComponent(frame, CoursesPanel.NAME));    assertEquals(Sis.COURSES_TITLE, frame.getTitle());    Image image = frame.getIconImage();    assertEquals(image, ImageUtil.create("/images/courses.gif")); } 

Create the class ImageUtilTest in the sis.util package. There are several ways to write a test against the create method. The best way would be to dynamically generate an image using a collection of pixels and write it out to disk. After the create method loaded the image, you could assert that the pixels in the loaded image were the same as the original pixel collection. Unfortunately, this is an involved solution, one that is best solved using an API that eases the dynamic creation of images.

A simpler technique is to allow the test to assume that a proper image already exists on disk under a recognized filename. The test can then simply assert that the image load was successful by ensuring that the loaded image is not null. The image must appear on the classpath.[1]

[1] I've modified the build.xml Ant script for this section to copy any file in src/images into classes/images each time a compile takes place. This allows you to quickly remove the entire contents of the classes directory without having to worry about retaining any images.

 package sis.util; import junit.framework.*; import java.awt.*; public class ImageUtilTest extends TestCase {    public void testLoadImage() {       assertNull(ImageUtil.create("/images/bogusFilename.gif"));       assertNotNull(ImageUtil.create("/images/courses.gif"));    } } 

This may seem weak, but it will provide an effective solution for now. You must ensure that the image courses.gif continues to stay in existence throughout the lifetime of the project. Instead of using an image file associcated with the SIS project, you might consider creating an image explicitly used for testing purposes.

Java provides several different ways to load and manipulate images. You will use the most direct.

 package sis.util; import javax.swing.*; import java.awt.*; public class ImageUtil {    public static Image create(String path) {       java.net.URL imageURL = ImageUtil.class.getResource(path);       if (imageURL == null)          return null;       return new ImageIcon(imageURL).getImage();    } } 

The class Class defines the method geTResource. This method allows you to locate resources, including files, regardless of whether the application is loaded from a JAR file, individual class files, or some other source such as the Internet.[2] The result of calling geTResource is a URLthe unique address of the resource.

[2] Technically, the image is loaded using the class loader as that of the Class object on which you call getresource. For applications that do not use a custom class loader, using the class loader of the ImageUtil class will work just fine.

Once you have a proper URL, you can pass it to the ImageIcon constructor to obtain an ImageIcon object. You can use ImageIcon objects for various purposes, such as decorating buttons or labels. Since the Frame class requires an Image object, not an ImageIcon, here you use the getImage method to produce the return value for create.

Note that the image filename passed to getresource is /images/courses.gifa path that uses a leading slash. This indicates that the resource should be located starting from the root of each classpath entry. Thus, if you execute classes from the directory c:\swing2\classes, you should put courses.gif in c:\swing2\classes\images. If you execute classes by loading them from a JAR file, it should contain courses.gif with a relative path of images.

Here are the changes to the initialize method in the Sis class.

 private void initialize() {    createCoursesPanel();    Image image = ImageUtil.create("/images/courses.gif");    frame.setIconImage(image);    frame.setSize(WIDTH, HEIGHT);    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    frame.getContentPane().add(panel); } 

You now have an acceptably pleasing interfaceat least from a visual standpoint. The revised layout is shown in Figure 1.

Figure 1. A Good Look




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