Layout Managers and Component Placement
The size of a component is not something to assume, because a layout manager often sets it and a user can resize the window.
A Drawing That Fits Only One Moment
Imagine you are writing a program that draws a shape on a graphical component. The drawing code is the part you wrote, and it is tempting to decide in advance how big the component is. You might type the number 300 into the drawing code and place every shape according to that number. On the screen, the program appears to work, and the number is easy to see while you are writing it. This lesson explains why that approach is a poor assumption, and what to do instead.
What do you think happens?
Before reading on, what kind of answer should a measurement of a component's size give? Think about what unit makes sense for a display made of individual dots.
Reveal answer
The size is measured in pixels.
The source states that getWidth() and getHeight() return a component's size in pixels. A display is made of individual dots, so a pixel is the natural unit for expressing a rectangle's width and height on it.
Why a Typed-In Size Goes Wrong
The size of a component is not something you can assume. The source gives two reasons. First, the size of a component is often set by a layout manager, which is the part of the system that decides where components are placed and how large they are. Second, a component can sit inside a window that the user resizes, so its size can change while the program is running. A number typed into the code has no connection to either of those forces. It describes one moment in one run of the program, not the component itself.
- The numbers 300 and 75 and 150 were guessed in advance.
- If the component is 200 pixels wide, the rectangle extends past the right edge.
- If the user stretches the window, the shapes stay at the same size and position.
The numbers in this example are guesses. They might match the component on one machine and fail on another, or match at startup and fail after the user stretches the window. The source is clear that the fix is not to guess more carefully. The fix is to ask the component for its size each time it draws.
Asking a Component for Its Size
getWidth() returns the horizontal extent, in pixels, of the rectangle the component occupies. getHeight() returns the vertical extent, in pixels, of that same rectangle.
Both methods are instance methods, which means you call them on the component itself. That is why the calls appear inside the component's own paintComponent() method without any extra object name in front of them. The values you get back should be stored in variables, and those variables should be used in the drawing code, rather than fixed numbers typed in by hand.
getWidth()andgetHeight()are read first, before any drawing.- The rectangle and oval are placed relative to w and h, so they follow the component's current size.
- The variables w and h are used in the drawing calls, not typed-in numbers.
Tracing paintComponent Step by Step
Follow the order of operations in the corrected method. The size is read first and the drawing comes after. This ordering is the core of the good practice the source recommends. As you trace each line, ask two questions: which line first gets a size value, and which later line depends on that value. Those two points are where a size-related error would have to show up, so they are the places to inspect when a drawing does not look the way you expected.
In the trace, the first place a size value is obtained is the pair of calls to getWidth() and getHeight(). The later drawing calls depend on the variables w and h. If the drawing calls used a typed-in number instead, the trace would show no link between the drawing and the component, which is exactly the problem described earlier.
How Layout and Resizing Change Size
The reason a checked size matters is that the size is not fixed for the life of the component. The source identifies two sources of change. The first is the layout manager, which often sets the size of a component. The second is the window that holds the component, which can be resized by the user. The source notes that a size can change in this second way even after the program is already running.
The diagram shows the order of events the source describes. A size is set by the layout and read by the drawing code. Later, the user may resize the window. After a resize, the drawing code has to read the size again, because the value it read earlier is no longer the component's size. This is why the source says to read the size at the time of drawing rather than relying on an old value.
Mistakes Beginners Make with Fixed Sizes
The following mistakes all come from one root belief: that the size of a component is already known. Each one is easy to make, and each one is corrected by reading the size first.
Common mistake Typing a fixed width such as 300 into the drawing code
What it looks likeg.drawRect(0, 0, 300, 300) used in a window that the user has made smaller
Why it failsThe number belongs to one moment in one run of the program, not to the component.
Do this insteadCall getWidth() and store the result in a variable, then use that variable in the drawing call.
Common mistake Reading the size once and reusing the stored value forever
What it looks likeSaving w in a field at startup and drawing with it after the user stretches the window
Why it failsA user can resize the window while the program runs, so the saved value becomes out of date.
Do this insteadRead the size again at the time of drawing, inside paintComponent().
Common mistake Assuming the layout manager has already set the size before the component is shown
Why it failsThe source notes that a layout manager often sets the size, so the value a program expects may not match what it reads at the moment of drawing.
Do this insteadRead the size from the component when drawing rather than predicting it in advance.
Practice: Spot the Size Assumption
Look at a paintComponent() method that draws a rectangle using the numbers 0, 0, 250, 120. Identify the line where a size value is assumed rather than read. Then rewrite the method so that the size is read first with getWidth() and getHeight(), stored in variables, and used in the drawing call. Explain in one sentence why the rewritten version still draws correctly after the user resizes the window.
Hints
- Find the drawing call and check whether its numbers come from
getWidth()orgetHeight()or were typed in. - Store each size in its own variable before the drawing line, and use those variables in place of 250 and 120.
- Think about whether the value you read is read at the time of drawing, which is when the window's current size applies.
Key Takeaways
- The size of a component is not something to assume, because a layout manager often sets it and a user can resize the window.
- Call
getWidth()andgetHeight()to find a component's size in pixels, store the results in variables, and do this before drawing. - Read the size again at the time of drawing, because the value can change while the program runs.
- In
paintComponent(), the size is read first and the drawing comes after, so a size-related error shows up at the line where the size is read and the line that uses it.