| Home > Core Java FAQ
> Abstract Window Toolkit FAQ |
| Abstract
Window Toolkit |
| Components,
Containers, and Peers(20) * Windows,
Frames, and Dialogs (19) * Miscellaneous
(31) |
| |
|
Q . What are the Component and Container classes?
|
Ans :
Component
and Container are the two
fundamental classes in the Abstract Window Toolkit (AWT); Component
instances (components) represent individual
user-interface elements, and Container
instances (containers) represent groups of components.
Component
is the abstract base class for all AWT user-interface elements
except menu components. A component can be presented on screen,
relocated, resized, or hidden; it can be drawn or printed; and it
can receive, handle, and deliver events. Components can also be
grouped and formatted in containers.
Container is an
abstract subclass of Component with the added
property of containing a group of components. Containers can also
contain other containers, which can contain other containers, and
so on to arbitrary depth. The term containment hierarchy
refers to this nested arrangement of containers and components
(sometimes also called a window hierarchy). Another useful bit of
terminology is to refer to a container as the parent of
each component that it contains.
It is important when learning to use
components and containers not to confuse the containment hierarchy
with the inheritance hierarchy.
Besides handling
the normal component tasks, a container also manages the
components it contains. A container can add components, remove
components, and (with the help of a layout manager) control the
layout of its components.
The Component and Container
classes constitute the backbone of the Java™ Abstract Window
Toolkit—you'll want to get to know these classes and their
methods well.
|
|
Q .
How do I control the positioning of my interface components?
|
Ans
:
Assign an
appropriate layout manager to each container in your interface, or
let the AWT assign default layout managers for you; the layout
manager automatically calculates layout positions for the
container's components.
In
the AWT, a container tracks the components that belong to
it, but calls on a LayoutManager object to control
their layout. LayoutManager is an interface. The
objects serving as layout managers come from classes that
implement the LayoutManager interface. (Starting with
the JDK 1.1, the LayoutManager2 interface extends LayoutManager
to provide better support for constraint-based layout managers.)
By setting the layout manager for a container, you are telling the
container what strategy to use when laying out its components.
A layout manager is responsible for
calculating the sizes and locations of the components in a
container. Different layout managers use different strategies for
this. A BorderLayout, for example, allocates five
distinct regions ("North", "South",
"West", "East", and
"Center") within a rectangle and allows one
component in each region. A GridLayout handles any
number of components by arranging them as a grid of equal-sized
cells. The number of rows and columns in the grid can be set when
the GridLayout instance is created. A FlowLayout
arranges its components much like the flow of words in a
paragraph: all in a single row if space permits, otherwise wrapped
around to one or more additional rows.
You can choose a layout manager for
each container. If you don't, the AWT assigns a default layout
manager depending on the class of your container. Window
and Window subclasses (such as Frame and
Dialog) use a BorderLayout by default; Panel
and Panel subclasses (including Applet)
use a FlowLayout by default. Typical steps for laying
out components are:
- creating your
components and containers
- setting the
layout manager for each container (or use the default)
- adding
components to the appropriate containers
You
can also define your own layout manager by implementing the LayoutManager
or LayoutManager2 interface. This is a nontrivial
undertaking, though, and requires a strong, detailed understanding
of existing layout managers. A rule of thumb is that you should
feel you understand 98 percent of the code in the existing JDK 1.1
layout managers (excluding, perhaps, GridBagLayout)
before writing your own.
|
|
Q .
What are inset values, and how do I set them?
|
Ans
:
Inset
values specify how much of a container's space is reserved for a
border, inside of which the container's components are arranged;
you set these values by overriding getInsets
(or insets in JDK 1.0.2) in your own
Container subclasses.
Each
container, for example a Frame instance, Panel
instance, or Applet instance, provides a rectangular
space in which to position the container's components. In the
default case, the components in a container can occupy the full
space of the container. Inset values, represented as an instance
of the Insets class, allow you to specify an amount
of space at one or more margins—a boundary inside which all
components must be placed. For example, a top inset of 10 means
that a component's top edge can be placed no higher than 10 units
(the space covered by 10 pixels) from the top of the container.
Inset values are useful for
containers that have border material surrounding their content
area. The Frame class, for instance, provides
top-level windows that have a title bar. The space for the title
bar is figured into the Insets instance, so that any
components placed in the Frame instance will neither
overlap nor lie underneath the title bar. Layout managers in
general take into account a container's inset values when
calculating where to place components.
You can specify inset values only
for a Container subclass that you define. This is
because containers have no setInsets method; instead,
your subclass must override the getInsets method (or
insets in JDK 1.0.2) to return the inset values you desire. For
example:
/* using JDK 1.1: */
public class myApplet extends java.applet.Applet {
// ...
public Insets getInsets() {
return new Insets(10, 6, 15, 3);
}
}
This code defines
an Applet subclass with the following inset values: top 10, left
6, bottom 15, and right 3.
Note: You shouldn't
override the inset values for Frame and Dialog
objects. These values are set by the native toolkit to ensure that
native border material, such as the title bar, does not cover any
content you place in the frame or dialog.
|
|
Q . How do I change the thickness of the line?
|
Ans
:
Java 1.1 and earlier only support 1 pixel wide lines. There's no easy way around this. You can, however, draw multiple, parallel lines offset from each other by one pixel:
public void paint(Graphics g) {
int x1=5;
int x2=278;
int y1=8;
int y2=93;
// Draw a ten pixel thick line
for (int i = -5; i < 5; i++) {
g.drawLine(x1+i,y1+i,x2+i,y2+i);
}
}
This isn't perfect. The ends of the line are excessively tapered. You really need to take the slope of the line into account when incrementing x and y, but this should give you the idea. If you're doing a lot of this, you can write a class or method to do it for you.
There are other hacks you can use. For example, a thick line is essentially a filled rectangle. Therefore you can calculate the endpoints of the rectangle and use fillPolygon() to draw it.
The real solution is going to have to wait for a more complete graphics API for Java, possibly in Java 1.2.
|
|
Q . Can I exert complete control over the size and placement of components in my interface?
|
Ans
:
Yes, you
can—but please don't.
The
AWT is designed to let layout managers handle layout details so
that resizing is handled automatically, and layouts behave
consistently across platforms. This is a central objective of Java
technology: writing applications once and running them anywhere.
You can, however, handle all your
layout work manually by not using any layout manager for your
container. To do so, set the container's layout manager to null.
For example:
myPanel.setLayout(null);
It
is then up to you to choose sizes and locations for all the
components, right down to the last pixel in your container.
Note: hand-done layouts are
strongly discouraged. What works on one specific
hardware-software platform can fail gracelessly on another.
Differences in native toolkits, font dimensions, and local
language preference can change a finely hand-tuned layout on one
platform into a mishmash of misalignments and overlaps on another.
|
|
Q . What do the invalidate and validate methods do?
|
Ans :
The invalidate
and validate methods control when a
container lays out its components.
Component
layouts in the AWT are dynamic. A change in one container, such as
resizing a text field or adding a button, can spread up the
containment hierarchy and trigger layout adjustments in many other
containers. For efficiency, the AWT splits the process into two
independently controllable parts:
- marking the
containers whose layouts are no longer valid
- redoing the
layout of containers marked as invalid
The
invalidate method handles the first step. It marks
containers as needing a new layout but doesn't perform that layout
itself. If a text field is resized, for instance, the AWT will
invoke invalidate on the text field and on the text
field's parent. When needed, the AWT propagates invalidate
calls up through the containment hierarchy.
All the real action happens in validate.
Invoking validate on a container checks whether that
container is marked as invalid, that is, in need of a new layout.
If the container is invalid, validate invokes the dolayout
method on it (or Layout in the JDK 1.0.2), and then
repeats this process on any components in the container. The
relayout process triggered by validate is thus
recursive, and can descend arbitrarily far down a containment
hierarchy.
If you plan to override validate
or layout in your own Container
subclasses, be careful of the following two implementation
details. First, although the effects of validate
descend the containment hierarchy, you cannot count on the validate
method itself being invoked on every container along the way.
Second, validate code, including calls to dolayout
(or layout in the JDK 1.0.2), executes within an AWT
synchronization lock. To avoid the risk of deadlock, you should
not acquire any further synchronization locks in your validate
or layout code (which, admittedly, is not always easy
to ensure).
For reference, Table 5.1 lists the
most common cases in which the AWT invokes invalidate
or validate on your behalf. Note that this table
presents implementation details of the JDK (1.0.2 and 1.1) that
may change in future releases, but these are useful to know
nevertheless.
Table
5.1: AWT Invocations of invalidate
and validate
| Class |
Method |
Invokes |
Container |
add |
invalidate |
Container |
remove |
invalidate |
Window |
pack |
validate |
Window |
show |
validate |
|
|
Q . Can I add new AWT components to objects already visible on the screen?
|
Ans
:
Yes, you
can change your program's interface as the program is running.
Like
other Java objects, AWT components can be created on the fly and
connected to existing objects. To appear on screen, the component
must be added to a container that is already on screen. You also
need to invoke validate on the container so that it
activates its layout manager to take the new component into
account. The validate method lets you control when
layouts are recalculated .
Following
is a code fragment that adds or removes a button from a panel
named centerPanel:
int buttonCount = 0;
Panel centerPanel = new Panel();
void addButton() {
++buttonCount;
centerPanel.add(new Button("button " + buttonCount));
centerPanel.validate();
}
|
|
Q . How can I tab between components?
|
Ans
: In JDK 1.0, you have to read the key press, and program it
explicitly. JDK 1.1 supports tab and shift-tab (previous field) automatically. The tab order is the order that the components were
added to the container.
|
|
Q . Can I add the same component to more than one container?
|
Ans
:
No; adding
a component to a container automatically removes it from any
previous parent (container).
The
AWT automatically maintains the strict rule that a component can
have only one parent. (This ensures that containment structures
are always trees, in graph theory terms. Trees are easier to
understand and easier to compute with than less-constrained
graphs.) The JDK (1.0.2 and 1.1) source code for adding a
component to a container includes the following lines:
/* in Container.java (JDK 1.0.2 and 1.1): */
// ... comp refers to the component being added
if (comp.parent != null) {
comp.parent.remove(comp);
}
It is an easy
mistake to add a component to more than one container, and then be
left wondering why the original container is behaving strangely.
|
|
Q . When I start a mouse drag inside a Component, and go outside
the Component, still dragging, the mouse events still get sent to the
Component, even though I am outside it. Is this a bug?
|
Ans
: No, it is the specified behavior. The Java API documentation says:
"... Mouse drag events continue to get sent to this component even
when the mouse has left the bounds of the component. The drag events continue until a mouse up event occurs...."
It is done for the convenience and ease of the application programmer.
It allows you to handle all drags from the place of origin. If you
don't want this, simply look at the coordinates of the mouseDrag Event,
and if they are outside the Component, ignore them.
|
|
Q . I use add(Component) to add Components to the Container. Is
there any way to explicitly set the z-order of these Components?
|
Ans
:
JDK 1.0 has no way to explicitly set the z-order of components. You
can try it heuristically, based on the browser you're using, or you can
use CardLayoutManager to ensure the panel you want is at the front.
In JDK 1.1, the z-order of components ("z-order" means "front-to-back" order, i.e. which window is in front of which) can be controlled by
using the the method add(Component comp, int index). By default,
components are added 0 to N. The method paint of class Container paints its visible components from N to 0.
|
|
Q . How can I get the dimensions and resolution of the screen?
|
Ans
:
In contrast, if you want to keep a counter variable that you increment repeatedly, by all means use an
int:
int count;
// ...
++count;
Finally, the Integer class lets you translate back and forth between Integer and int representations of the same value:
int i = 5;
Integer myInteger = new Integer(i);
int j = myInteger.intValue();
Note: The extra effort required to convert between int and Integer can be a nuisance, but is consistent with Java's emphasis
on type safety.
|
|
Q .
How can I place an outline around a group of components to show explicitly how the components are grouped?
|
Ans
:
You can
write your own Panel
subclass that draws an outline and specifies appropriate inset
values to ensure that the panel's components stay inside the
outline.
Panel,
as a subclass of Component, includes a paint
method that draws the panel on the screen. Panels typically just
draw a background color on which their components are placed, but
you can define your own paint method to draw whatever
you like in the panel. The drawRect method in the Graphics
class provides a very simple means to draw an outline. The
following paint method, for example, uses drawRect
to provide a blue outline:
/* using JDK 1.1: */
public void paint(Graphics g) {
Dimension size = getSize();
g.setColor(Color.blue);
g.drawRect(2, 2,
size.width - 5, size.height - 5);
}
Anything
you draw on a panel using paint, however, can be
covered by components contained in the panel. Therefore, you need
to inform the panel's layout manager that space at the margins
should be left free for the outline. To do this, override the getInsets
method to provide the necessary space, such as:
/* using JDK 1.1: */
public Insets getInsets() {
return new Insets(5, 5, 5, 5);
}
To run in the JDK
1.0.2, these code samples would use the method names size
and insets in place of getSize and getInsets.
|
|
Q . What are those preferredSize() and minimumSize() methods in Component?
|
Ans
:
Those methods allow a LayoutManager to calculate the preferred and
minimum sizes of the Components it is arranging. You can control the
values that the LayoutManager gets by creating subclasses of the Components you are using and overriding these methods. You don't call
them; you override them and they are called on your behalf.
|
|
Q .
Why isn't my component showing up at all? Or in some cases not until I resize the main window?
|
Ans
: The initial sizes of components are not always exactly what the
programmer would expect. When a component doesn't show up, often it is
getting added to its parent, but with a size of 0x0. Even when
getPreferredSize gives a non-zero value. If this seems to be what's happening, try calling
setSize(getPreferredSize()). If that doesn't seem to be the problem, look into your layout manager.
|
|
Q .
When I change some component (e.g. a new label on a button) I don't see the change on the screen immediately even if I repaint().
|
Ans
:
You need to add a call to the paintImmediately(x,y,w,h) method of
JComponent. That repaints the component completely before continuing
execution. On pre-JDK 1.2 systems, use:
invalidate();
validate();
They cause the component hierarchy to be marked as needing to be laid out again, and the validate causes that to be done. It may be
expensive, but is a way of getting the peers to recalculate size and to
do what is needed to bring the display up to date. It has limitations:
it doesn't cause an immediate screen update when invoked from an event
handler, where paintImmediately() does.
|
|
Q . Clipping.. How tod do?
|
Ans
: java.awt.Graphics.clipRect(int, int, int, int) and
related methods are hopelessly flawed, at least as of 1.0. Ignore
them completely.
Instead if you need to do clipping, create separate offscreen
Images for each clipping region. Each Image should be the size of
the clipping region you desire. Draw into those offscreen images,
and then copy them onto the appropriate section of the of your
applet window using java.awt.Graphics.drawImage(). Some coordinate
conversion will almost certainly be necessary.
If the background image isn't a simple color then you'll first
need to copy the appropriate part of that image to your offscreen
clipping Image. You can do this by drawing your background Image
into your offscreen region with Graphics.drawImage() and a
suitable shifting of coordinates.
This all works for rectangular regions only since all Images are
rectangular. More complicated geometries can be faked if all but
one section contains only simple colors.
|
|
Q .
What are AWT peers?
|
Ans
:
Peers are
native-platform on-screen counterparts to AWT components.
The
AWT provides a cross-platform Application Programming Interface
(API) for managing user-interface elements and behaviors. To
provide a native look and feel, though, the AWT components rely on
user-interface elements from the native-platform toolkits to draw
themselves on screen and to provide an initial layer of event
processing. These native counterparts are called peers.
The java.awt.peer package defines interfaces through
which Java components can control and otherwise communicate with
their peers.
The AWT peer interfaces are
implemented by platform-specific peer classes that access the
platform's own toolkit using native methods (that is, methods
implemented in a language other than Java). For example, an AWT
button has three Javalanguage layers:
Button,
a class in the java.awt package
ButtonPeer,
an interface in the java.awt.peer package
- a
platform-specific button peer class containing native methods
When
you write a Java program, you typically only concern yourself with
the AWT classes or your own subclasses of them. When the program
is run, the AWT selects the appropriate peer objects and hooks
them up to your AWT objects. This is the way that your Java
program can create and control a Motif button when run on Solaris,
a Win32 button when run on Microsoft Windows NT/95, or a Macintosh
button when run on MacOS.
Java programmers should rarely have
to deal directly with AWT peer objects, but knowing about them
helps to avoid some subtle pitfalls:
- Some AWT
components depend on their peer objects for information such
as minimum and preferred sizes. In general, you should make
sure a peer exists before you try to get size information
about your component.
- Layout
operations check component sizes; hence, they depend on peers.
- Some methods,
such as
getGraphics in the Component
class, return null if the component doesn't yet
have a peer.
|
|
Q . What are peer "classes"?
|
Ans
: Peer classes exist mainly for the convenience of the people who wrote the Java environment. They help in translating between the AWT user interface and the native (Windows, OpenWindows, Mac etc.) interfaces. Unless you're porting Java to a new platform you shouldn't have to use them.
|
|
Q .
When are peers created and destroyed?
|
Ans
:
The
creation and destruction of peers is in general triggered by add
and remove methods in class Container;
but there are further complications.
Creating
an AWT component does not automatically create a peer for that
component. Instead, the creation of peers waits until a component
is rooted in a containment hierarchy that reaches up to a
top-level window (more specifically, a Frame
instance). This is necessary for some native toolkits—such as
Motif—that require their interface components to be traceable to
a top-level window. The AWT builds two well-formed component trees
side by side—an AWT component tree and a peer component
tree—and the AWT respects the constraints on both sides in the
process.
There are two stages at which peers
can be created. On the one hand, a component's peer can be created
as soon as the component is added to a container, provided that
the container itself already has a peer. This is the case where a
peer tree descending from a frame (or other top-level window)
already exists and is thus ready to grow further. The top-level
window itself will get a peer when either pack or show
is invoked on it. (The creation of peers is somewhat of a side
effect of pack and show. Window's
pack method resizes a window to the preferred size
determined by the window's layout manager. Window's how
method brings a window on screen if it wasn't visible, or brings
the window to the front of other windows if the window is already
on screen.)
Alternatively, a component can be
contained, possibly many layers deep, in a Panel
instance or other container that isn't yet linked into a frame. In
this case, a whole subtree of AWT components exists without a
corresponding peer subtree. This subtree is then subject to the
first pattern described. As soon as it is added to a container
with a peer, it will recursively descend through itself to create
peers for all the subtree components.
The destruction of peers is simpler:
a component's peer is destroyed when the component is removed from
its container.
Note: The lifetime of AWT
components is separate from that of any peers. For example, you
can add and remove a component from a container several times, but
each time you do, you create and destroy a separate peer.
|
|