Pitfall 1: When setSize() doesn't work
Most developers stumble upon pitfalls sequentially as they become more experienced with Java. The setSize() pitfall usually presents itself soon after Java developers begin serious GUI development -- specifically, when they first attempt to set the size of a custom component. BadSetSizeApplet below intends to create a simple custom button, sized at 100 pixels by 100 pixels. Here is the code to create our custom button: classCustomButton extends Button
{
public CustomButton(String title)
{
super(title);
setSize(100,100);
}
}
In the constructor, developers often assume that they can use setSize()(width, height), as they do when sizing a frame. But this code will only work in certain situations -- here, setSize() will fail to correctly size the component. When we place our custom button in the applet with other components using a simple grid layout, we get the results below. Our button is 66 by 23, not 100 by 100! What happened to our call to setSize()? The method was executed, of course. However, it did not give the final word on the size of our component.
Button size before display: java.awt.Dimension[width=100,height=100]
Button size after display: java.awt.Dimension[width=66,height=23]
You will find the complete source code for BadSetSizeApplet.java, and for all applets discussed in this article, in Resources.
Let's examine the correct approach to sizing a component.
Our code failed because after we created the component, the layout manager (GridLayout) reshaped the component in accordance with its own rules. This presents us with several solutions. We could eliminate the layout manager by calling setLayout(null), but since the layout manager provides numerous benefits to our code (it allows us to automatically resize our user interface, even if the user resizes the window), this is a poor remedy. Another alternative would be to call setSize() after the layout manager has completed its work. This is just a quick fix: calling repaint() would change the size, but it would change again when the browser was resized. That leaves us with only one real option: work only with the layout manager to resize the component. Below we rewrite our custom component: class CustomButton2 extends Button
public CustomButton2(String title)
{
super(title);
// setSize(100,100); - unnecessary
}
public Dimension getMinimumSize()
{ return new Dimension(100,100); }
public Dimension getPreferredSize()
{ return getMinimumSize(); }
}
Our custom component overrides the getMinimumSize() and getPreferredSize() methods of the Component class to set the component size. The layout manager implements those methods to decide how to size an individual component. Some layout managers will disregard the methods' advice, if their pattern calls for that. For example, if this button was placed in the center of a BorderLayout, the button would not be 100 by 100, but would stretch to fit the available center space. GridLayout will abide by those sizes and anchor the component in the center. The GoodSetSizeApplet below uses the CustomButton2 class.
Button size before display: java.awt.Dimension[width=0,height=0]
Button size after display: java.awt.Dimension[width=100,height=100]
Interestingly, our solution did not involve the setSize() method. This pitfall stems from the design complexity of a cross-platform user interface, and a developer's unfamiliarity with the chain of events necessary to display and resize an interface. Unfortunately, the supplied documentation of setSize() fails to suggest these prerequisites. This solution also highlights the importance of properly naming methods and parameters. Should you use setSize() when you only need to set internal values that may or may not be used by your display mechanisms? A better choice would be setInternalValues(), which at least clearly warns a developer of the limited guarantee this method offers.
No comments:
Post a Comment