So far, we have been using components in their default configuration. We have moved them around, made them different sizes, and let the layout managers place them for us. One of the changes we can make to the components is to give them a little color and change the font. With Java and Visual J++, this is an easy task that you can use to dramatically change the look of your program.
In this chapter, a sample application that displays some text will be used to illustrate the use of color and font. In addition, two buttons will be added. The first button will bring up a dialog that lets the user set the color of text to display. With this dialog, the user will be able to use a preset default color, or set a custom color. The second button will bring up a dialog that lets the user select the font to be used for the text. This dialog allows the selection of the type of font, the size of the font, and optional attributes of bold and italic.
The best way to explore this topic is to dive right in. The base application is shown in Figure 16.1.
Figure 16.1 : The base applet used to set the color and font.
Listing 16.1 shows the source to this applet.
Listing 16.1. EX16A.java.
import java.applet.*;
import java.awt.*;
public class EX16A extends Applet
{
protected Button SetColorButton = new Button("Set Color");
protected Button SetFontButton = new Button("Set Font");
protected String MyText = new String("This is the text being modified.");
public void init()
{
add(SetColorButton);
add(SetFontButton);
}
public void paint(Graphics g)
{
g.drawString(MyText, 20, 100);
}
}
The applet-derived class contains two buttons: SetColorButton and SetFontButton. The init method is straightforward and simply adds the components to the applet. In addition, MyText is displayed in the paint method to have something to operate on. Easy stuff.
The basis for setting the color of a number of items is the Color class. This section covers the definition of the Color class, a generic dialog used to set the color, and an applet putting it to use.
The Color class is a simple class that can hold any color. It does this by storing a color based on the concentration of red, green, and blue.
To create a color, use one of the three Color constructors:
public Color(float r, float g, float b); public Color(int rgb); public Color(int r, int g, int b);
The first of these constructors takes the three parts in floating point format. The concentration of each element expressed with values ranging from 0 to 1.0. The next two constructors will probably be used more often because their values are expressed as integer values ranging from 0 to 255. The first of the integer constructors has the values encoded in a single integer-bits 16-23 represents red, 8-15 represents green, and 0-7 represents blue. The final integer constructor simply has the values separated into three manageable parts.
Here are some examples of declaring colors:
Color black = new Color(0, 0, 0); Color white = new Color(255, 255, 255); Color coolPurple = new Color(0.345, 0, 0.5);
The values for each color indicate the concentration of the color;
therefore, the lower the number, the lower the concentration.
And conversely, the higher the value, the higher the concentration.
The examples express this with black being an absence
of color, and white being a combination of all colors.
The rest of the colors fall somewhere in between.
| NOTE |
Remember that colors are only as good as the device displaying them. Therefore, the following two colors might or might not be the same. Color coolPurple = new Color(102, 0, 128); |
So that you are not completely on your own, the Color
class provides the following predefined colors, which are instances
of the Color class and can be used whenever a Color
class is used:
black magenta blue orange cyan pink darkGray red gray white green yellow lightGray
Once you have an instance of Color, there are a variety
of things that can be done with the color. The public methods
are described in Table 16.1.
| Member | Purpose |
| brighter() | Returns a color brighter than the current color. |
| darker() | Returns a color darker than the current color. |
| equals(Object) | Compares two color objects for the same color. |
| getBlue() | Returns the blue component. |
| getColor(String) | Returns the color of the named system property. |
| getColor(String, Color) | Returns the color of the named system property, specifying a default if not found. |
| getColor(String, int) | Returns the color of the named system property, specifying a default if not found. |
| getGreen() | Returns the green component. |
| getHSBColor(float, float, float) | Returns a color with the given hue, saturation, and brightness. |
| getRed() | Returns the red component. |
| getRGB() | Returns the components combined in a single integer. |
| hashCode() | Returns a hash code representing the color. |
| HSBtoRGB(float, float, float) | Returns an RGB value for a color specified by hue, saturation, and brightness. |
| RGBtoHSB(int, int, int, float) | Returns values for hue, saturation, and brightness for a color specified by an RGB value. |
| toString() | Returns a string representation of the color. |
Given the knowledge of how to construct a color given the components, the task is to develop a dialog that enables the user to select his own color. Figure 16.2 contains the dialog at work.
Figure 16.2 : Set Color dialog of EX16B.
With the dialog, the user can select a custom color by specifying the color components using the scrollbars. Adjusting the scrollbars with the arrows or by dragging the thumbnail will automatically adjust the value to the right of each scrollbar. If the user selects a default color from the choice list, this automatically adjusts the scrollbar values to the appropriate values so that the user can see what components make up the default values. A sample swatch of the color is displayed in the box in the lower right of the dialog.
The first step in creating this dialog was to use the Resource
Wizard to design and lay out the components of the class. For
more information on using the Resource Wizard, refer to Chapter
7, "Saving Time with the Resource Wizard." This process
produced the resource file SetColor.rct. We then used
the Tools | Java Resource Wizard to produce the files DialogLayout.java
and SetColorControls.java.
| CAUTION |
When using the Dialog Editor, be sure to only place controls on your container classes that have a direct translation to Java components. Refer to Table 7.1, in Chapter 7 for a list of available controls and the corresponding Java components. |
DialogLayout is a layout manager that is based on Dialog Logical Units (DLUs). This class is used by the SetColorControls class to place components at a specific location on the dialog using DLUs. To learn more about layout managers, see Chapter 5 "Java's User Interface Components."
Listing 16.2 shows the contents of the SetColorControls class generated by the Java Resource Wizard.
Listing 16.2. SetColorControls.java.
import java.awt.*;
import DialogLayout;
public class SetColorControls
{
Container m_Parent = null;
boolean m_fInitialized = false;
DialogLayout m_Layout;
// Control definitions
//--------------------------------------------------------------------------
Button OkButton;
Button CancelButton;
Scrollbar RedScrollbar;
Label IDC_STATIC1;
Label IDC_STATIC2;
Scrollbar GreenScrollbar;
Scrollbar BlueScrollbar;
Label IDC_STATIC3;
Label IDC_STATIC4;
Choice StandardColorChoice;
Label RedValue;
Label GreenValue;
Label BlueValue;
// Constructor
//--------------------------------------------------------------------------
public SetColorControls (Container parent)
{
m_Parent = parent;
}
// Initialization.
//--------------------------------------------------------------------------
public boolean CreateControls()--
{
// CreateControls should be called only once
//----------------------------------------------------------------------
if (m_fInitialized || m_Parent == null)
return false;
// m_Parent must be extended from the Container class
//----------------------------------------------------------------------
if (!(m_Parent instanceof Container))
return false;
// Since a given font may not be supported across all platforms, it
// is safe to modify only the size of the font, not the typeface.
//----------------------------------------------------------------------
Font OldFnt = m_Parent.getFont();
if (OldFnt != null)
{
Font NewFnt = new Font(OldFnt.getName(), OldFnt.getStyle(), 8);
m_Parent.setFont(NewFnt);
}
// All position and sizes are in dialog logical units, so we use a
// DialogLayout as our layout manager.
//----------------------------------------------------------------------
m_Layout = new DialogLayout(m_Parent, 220, 116);
m_Parent.setLayout(m_Layout);
m_Parent.addNotify();
Dimension size = m_Layout.getDialogSize();
Insets insets = m_Parent.insets();
m_Parent.resize(insets.left + size.width + insets.right,
insets.top + size.height + insets.bottom);
// Control creation
//----------------------------------------------------------------------
OkButton = new Button ("OK");
m_Parent.add(OkButton);
m_Layout.setShape(OkButton, 111, 98, 50, 14);
CancelButton = new Button ("Cancel");
m_Parent.add(CancelButton);
m_Layout.setShape(CancelButton, 166, 98, 50, 14);
RedScrollbar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 1, 0, 99);
m_Parent.add(RedScrollbar);
m_Layout.setShape(RedScrollbar, 39, 10, 136, 11);
IDC_STATIC1 = new Label ("Red", Label.LEFT);
m_Parent.add(IDC_STATIC1);
m_Layout.setShape(IDC_STATIC1, 10, 11, 28, 8);
IDC_STATIC2 = new Label ("Green", Label.LEFT);
m_Parent.add(IDC_STATIC2);
m_Layout.setShape(IDC_STATIC2, 10, 24, 28, 8);
GreenScrollbar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 1, 0, 99);
m_Parent.add(GreenScrollbar);
m_Layout.setShape(GreenScrollbar, 39, 23, 136, 11);
BlueScrollbar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 1, 0, 99);
m_Parent.add(BlueScrollbar);
m_Layout.setShape(BlueScrollbar, 39, 36, 136, 11);
IDC_STATIC3 = new Label ("Blue", Label.LEFT);
m_Parent.add(IDC_STATIC3);
m_Layout.setShape(IDC_STATIC3, 10, 37, 28, 8);
IDC_STATIC4 = new Label ("Set Color To:", Label.LEFT);
m_Parent.add(IDC_STATIC4);
m_Layout.setShape(IDC_STATIC4, 11, 62, 50, 8);
StandardColorChoice = new Choice ();
m_Parent.add(StandardColorChoice);
m_Layout.setShape(StandardColorChoice, 75, 60, 87, 52);
RedValue = new Label ("0", Label.LEFT);
m_Parent.add(RedValue);
m_Layout.setShape(RedValue, 190, 11, 18, 8);
GreenValue = new Label ("0", Label.LEFT);
m_Parent.add(GreenValue);
m_Layout.setShape(GreenValue, 190, 24, 18, 8);
BlueValue = new Label ("0", Label.LEFT);
m_Parent.add(BlueValue);
m_Layout.setShape(BlueValue, 190, 37, 18, 8);
m_fInitialized = true;
return true;
}
}
The member variable m_Parent holds the container class in which the controls are placed. m_Layout is the layout manager that is used to position the controls on the container. Additionally, there is a component instance for each of the controls placed on the dialog in the Dialog Editor.
The two member methods of this class are simple, yet effective. The constructor saves the parent of all of the components. CreateControls does some basic housekeeping to make sure the parent exists and that it is a type of container. It then sets up the layout manager for the parent. Finally, it places all the controls on the container, based on the designed DLUs.
The next step is examining the class used for the dialog. This dialog will contain an instance of the controls class mentioned above and the desired code necessary to coordinate the activities on the dialog. Listing 16.3 contains the SetColorDialog class of EX16B.
Listing 16.3. SetColorDialog class of EX16B.
class SetColorDialog extends Dialog
{
protected Color selectedColor;
protected ColorData cData;
protected SetColorControls ctrls;
protected Rectangle sampleRect;
public SetColorDialog(Frame parent, ColorData cData)
{
super(parent, "Set Color", true);
setFont(new Font("Dialog", Font.PLAIN, 16));
// don't let the user change the size
setResizable(false);
resize(220, 116);
// create the controls for the dialog
ctrls = new SetColorControls(this);
ctrls.CreateControls();
FillSetColorTo();
SetupScrollbars();
// save the data and start off with the current color
this.cData = cData;
selectedColor = new Color(cData.GetColor().getRGB());
}
public boolean action(Event evt, Object arg)
{
boolean result = false; // asume no action
if ("OK".equals(evt.arg)) {
cData.SetColor(selectedColor);
dispose(); // close the dialog
}
if ("Cancel".equals(evt.arg))
dispose(); // close the dialog
return result;
}
public boolean handleEvent(Event evt)
{
boolean result = false; // assume no action
// call the superclass for normal processing
result = super.handleEvent(evt);
// process the event here if not handled yet
if (!result) {
if (evt.target == ctrls.RedScrollbar) {
handleScrollbarEvent(ctrls.RedScrollbar, ctrls.RedValue);
result = true;
}
else if (evt.target == ctrls.GreenScrollbar) {
handleScrollbarEvent(ctrls.GreenScrollbar, ctrls.GreenValue);
result = true;
}
else if (evt.target == ctrls.BlueScrollbar) {
handleScrollbarEvent(ctrls.BlueScrollbar, ctrls.BlueValue);
result = true;
}
else if (evt.target == ctrls.StandardColorChoice) {
handleChoiceEvent();
result = true;
}
}
return result;
}
public void paint(Graphics g)
{
super.paint(g); // let normal processing happen
if (selectedColor != null) {
if (sampleRect == null) {
// determine where we can put the sample color
sampleRect = ctrls.StandardColorChoice.bounds();
sampleRect.x += sampleRect.width + 10;
sampleRect.width = 20;
sampleRect.height = 20;
}
g.setColor(selectedColor);
g.drawRect(sampleRect.x, sampleRect.y,
sampleRect.width, sampleRect.height);
g.fillRect(sampleRect.x, sampleRect.y,
sampleRect.width, sampleRect.height);
}
}
protected void FillSetColorTo()
{
ctrls.StandardColorChoice.addItem("black");
ctrls.StandardColorChoice.addItem("blue");
ctrls.StandardColorChoice.addItem("cyan");
ctrls.StandardColorChoice.addItem("darkGray");
ctrls.StandardColorChoice.addItem("gray");
ctrls.StandardColorChoice.addItem("green");
ctrls.StandardColorChoice.addItem("lightGray");
ctrls.StandardColorChoice.addItem("magenta");
ctrls.StandardColorChoice.addItem("orange");
ctrls.StandardColorChoice.addItem("pink");
ctrls.StandardColorChoice.addItem("red");
ctrls.StandardColorChoice.addItem("white");
ctrls.StandardColorChoice.addItem("yellow");
ctrls.StandardColorChoice.addItem("custom");
}
protected void SetupScrollbars()
{
ctrls.RedScrollbar.setValues(0, 10, 0, 255);
ctrls.GreenScrollbar.setValues(0, 10, 0, 255);
ctrls.BlueScrollbar.setValues(0, 10, 0, 255);
}
protected void handleScrollbarEvent(Scrollbar sBar, Label sBarValue)
{
int value = sBar.getValue();
// set the text value according to the scroll bar
sBarValue.setText(String.valueOf(value));
// reset the actual sample
SetSampleColor();
// changing of the scroll bar means it is custom
ctrls.StandardColorChoice.select("custom");
}
protected void handleChoiceEvent()
{
Color standardColor = null; // holds found color
String name = ctrls.StandardColorChoice.getSelectedItem();
// check for each of the standard colors
if (name == "black")
standardColor = new Color(Color.black.getRGB());
else if (name == "blue")
standardColor = new Color(Color.blue.getRGB());
else if (name == "cyan")
standardColor = new Color(Color.cyan.getRGB());
else if (name == "darkGray")
standardColor = new Color(Color.darkGray.getRGB());
else if (name == "gray")
standardColor = new Color(Color.gray.getRGB());
else if (name == "green")
standardColor = new Color(Color.green.getRGB());
else if (name == "lightGray")
standardColor = new Color(Color.lightGray.getRGB());
else if (name == "magenta")
standardColor = new Color(Color.magenta.getRGB());
else if (name == "orange")
standardColor = new Color(Color.orange.getRGB());
else if (name == "pink")
standardColor = new Color(Color.pink.getRGB());
else if (name == "red")
standardColor = new Color(Color.red.getRGB());
else if (name == "white")
standardColor = new Color(Color.white.getRGB());
else if (name == "yellow")
standardColor = new Color(Color.yellow.getRGB());
// set the scrollbar if the color is not custom
if (standardColor != null) {
SetScrollbarValue(ctrls.RedScrollbar,
ctrls.RedValue, standardColor.getRed());
SetScrollbarValue(ctrls.GreenScrollbar,
ctrls.GreenValue, standardColor.getGreen());
SetScrollbarValue(ctrls.BlueScrollbar,
ctrls.BlueValue, standardColor.getBlue());
// reset the actual sample
SetSampleColor();
}
}
protected void SetScrollbarValue(Scrollbar sBar,
Label sBarValue, int value)
{
sBar.setValue(value); // set the value
// set the text value according to the new value
sBarValue.setText(String.valueOf(value));
}
protected void SetSampleColor()
{
int red = ctrls.RedScrollbar.getValue();
int green = ctrls.GreenScrollbar.getValue();
int blue = ctrls.BlueScrollbar.getValue();
selectedColor = new Color(red, green, blue);
repaint();
}
}
Let's take a look at the constructor. It takes as its parameter the parent frame, which is required by all dialogs, and a ColorData class instance. The next section will cover the ColorData class in detail. For now, it is enough to know that this is the mechanism used to pass the initial color to the dialog and the selected color back to the calling class.
Inside the constructor, the first step is to call the superclass constructor. This call is required to be the first line in the constructor. Next, the font is set for the dialog. This is always a good idea since the controls class is based on the current font of the container dialog. The call to setResizeable disables the user from changing the size of the dialog. This is important because a mechanism to resize the controls is not built into the dialog. Next, create the controls using the classes previously discussed. Finally, initialize some of the controls with FillSetColorTo and SetupScrollbars method calls, and set the starting color to the color passed in.
The SetColorControls class is concerned only with the actual placement of the components on the dialog. It does not take care of any configuration specific to the application or initialization. FillSetColorTo loads the choice box for the standard default colors. Notice how ctrls, the SetColorControl class instance, is used to reference the choice component. In addition to the standard colors, the choice of "custom" is used when the scrollbars are modified directly. SetupScrollbars is used to initialize the ranges for all the scrollbars from a range of 0 to 255, which corresponds to the integer ranges from the Color class.
The paint method is used to display the sample color in the lower right of the dialog, using the currently selected color. SetColor, of the Graphics instance g, is called to set the color. Because Graphics contains only a single color, that color is used for all subsequent drawings. Thus, the sample color rectangle is filled with the currently selected color. Later, you will see that when the color changes, the dialog forces a redrawing of the dialog, using repaint, to update the sample color.
The button presses are caught and acted upon in the action method. If the OK button is pressed, the currently selected color is saved in the ColorData class and the dialog resources are disposed of, causing the dialog to close. If the Cancel button is pressed, the dialog resources are also disposed of, once again causing the dialog to close.
Perhaps the busiest of the methods is the handleEvent method. The first thing this does is try to get out of doing anything by calling the superclass with the current event. This allows the action processing to occur. Then, if there is something left to do with the event, this method checks to see whether the event involves one of the scrollbars or the choice.
If the scrollbar was modified, handleScrollbarEvent updates the label next to the scrollbar with the current scrollbar value, updates the sample color being displayed by calling SetSampleColor, and forces the selected item in the choice to be "custom." SetSampleColor takes the values from the three scrollbars and creates a new color based on these values. It then forces the sample rectangle to be redrawn with the newly selected color by calling the repaint method of the dialog.
If a new item in the choice is selected, handleChoiceEvent is called. Unfortunately, since switch does not work on strings, the majority of this code is looking for the standard color that was selected. If a standard color is found, SetScrollbarValue is called for each of the components. In this way, the user can see exactly what goes into making the standard colors. Then, SetSampleColor is used to update the sample rectangle.
That wasn't too bad. You now have a reusable dialog that can be used in a variety of situations in which you want to let the user choose his own colors. The next section covers how the dialog actually communicates with the instantiating class on what color was selected, using the ColorData class.
One of the hardest aspects of using dialogs to collect information from users is relaying the collected information back to the calling class. One option is to pass the calling class into the dialog class and let the dialog call method(s) set the collected data in the calling class. Let's take a quick look and consider EX16C, shown in Listing 16.4.
Listing 16.4. EX16C.
import java.applet.*;
import java.awt.*;
public class EX16C extends Applet
{
protected InputDialog IDialog;
protected TextField MyField = new TextField(20);
public void SetMyFieldText(String str)
{
MyField.setText(str);
}
public void init()
{
add(new Button("Set Text"));
add(MyField);
// set up the frame for the dialog
Frame frame = new Frame();
frame.resize(250, 100);
IDialog = new InputDialog(frame, this);
}
public boolean action(Event evt, Object obj)
{
boolean result = false; // asume no action
if ("Set Text".equals(obj)) {
IDialog.show();
result = true;
}
return result;
}
}
class InputDialog extends Dialog
{
protected TextField InputText;
protected EX16C aplt;
public InputDialog(Frame parent, EX16C aplt)
{
super(parent, "Input into my field please...", true);
this.aplt = aplt;
InputText = new TextField();
add("North", InputText);
Panel p = new Panel();
p.add(new Button("OK"));
p.add(new Button("Cancel"));
add("South", p);
pack();
resize(250, 100);
}
public boolean action(Event evt, Object arg)
{
boolean result = false; // asume no action
if ("OK".equals(evt.arg)) {
aplt.SetMyFieldText(InputText.getText());
dispose(); // close the dialog
}
if ("Cancel".equals(evt.arg))
dispose(); // close the dialog
return result;
}
}
When a button is pressed, this applet shows a dialog that gathers the text that is displayed in the text field of the applet. The importance of this applet is the fact that the dialog is passed the applet in the constructor. When the buttons on the dialog are pressed to close the dialog, the information collected is put back into the applet (see InputDialog.action). This concept is not very practical for generic dialogs because it has very high coupling between the classes involved.
An alternate approach would be to use Observer and Observable classes from the java.util package. For a complete description of these classes along with other classes found in the java.util package, see Chapter 11, "The Java Utility Classes."
Let's go back to the sample applet, EX16B. Listing 16.5 shows the observable data that is passed to the Set Color dialog.
Listing 16.5. Observable Color data class of EX16B.
class ColorData extends Observable
{
protected Color clr = new Color(Color.black.getRGB());
public Color GetColor()
{
return clr;
}
public void SetColor(Color clr)
{
this.clr = clr; // save the color
// flag the change an notify the on-lookers
setChanged();
notifyObservers();
}
}
The sole purpose of this class is to pass data from the applet to the Set Color dialog. The single piece of data is the color of the text. Obviously, more complicated dialogs would have more data.
GetColor is just an access method to the color. Both the applet and the dialog call this method-the applet to determine the color of the text to be displayed, while the Set Color dialog calls it to determine the starting color for the dialog. SetColor is called by the Set Color dialog when the user presses the OK button after making a selection. This method first saves the new color, then makes the Observable-specific calls that mark that the data has changed, then notifies all of the class's observers that the data has changed.
The only thing that the applet needs to do is to implement the Observer interface. Listing 16.6 shows the code from the EX16B applet pertinent to being an Observer.
Listing 16.6. Observer-specific code of EX16B applet.
public class EX16B extends Applet implements Observer
{
...
protected ColorData cData = new ColorData();
...
public void init()
{
...
// let the applet observe the color
cData.addObserver(this);
...
}
...
public void update(Observable o, Object arg)
{
// since the paint method already looks at cData, simply
// force a repaint of the dialog to pick up the new color
repaint();
}
}
The applet instantiates an instance of the Observable color data that is used to paint the text. In init, it tells Observable that it is an observer by making a call to addObserver and passing the applet. update is the only method in the Observer interface and must be defined. This method is what gets notified when Observable changes. Since we are the only observer of the color, all we have to do is repaint the applet and the new color will take effect.
Why go through the overhead of using the Observer/Observable relationship? That's easy. One of the problems with showing a dialog is that the actual lifespan of the dialog is asynchronous with respect to the actions of the applet. Therefore, this interface is an easy way to get the dialog to tell the applet when it modifies the common data. And, more importantly, this process decouples the dialog and the applet so that the dialog can be used in any applet for a variety of different color settings. Now, let's look at the final part of the applet that uses the Set Color dialog.
To use the Set Color dialog, we go back to the basic applet with two buttons and some text that is being displayed. Listing 16.7 shows the applet class EX16B.
Listing 16.7. Applet class EX16B.
public class EX16B extends Applet implements Observer
{
protected Button SetColorButton = new Button("Set Color");
protected Button SetFontButton = new Button("Set Font");
protected String MyText = new String("This is the text being modified.");
protected Frame frame;
protected ColorData cData = new ColorData();
protected SetColorDialog SetColorDlg;
public void init()
{
// set up the set color dialog frame
frame = new Frame();
frame.resize(1, 1);
// let the applet observe the color
cData.addObserver(this);
// add the controls to the applet
add(SetColorButton);
add(SetFontButton);
}
public void paint(Graphics g)
{
// use the color data to set the text color
g.setColor(cData.GetColor());
g.drawString(MyText, 20, 100);
}
public boolean action(Event evt, Object obj)
{
boolean result = false; // asume no action
if ("Set Color".equals(obj)) {
SetColorDlg = new SetColorDialog(frame, cData);
SetColorDlg.show();
result = true;
}
return result;
}
public void update(Observable o, Object arg)
{
// since the paint method already looks at cData, simply
// force a repaint of the dialog to pick up the new color
repaint();
}
}
The init method sets up the frame to use for the dialog, sets up the data used for the dialog, and places the buttons on the applet. paint actually uses the color by setting the color in the Graphics and then calls DrawString to do the drawing. Again, the Graphics instance will use the color to do all subsequent actions. action is a straightforward implementation of displaying the dialog in response to a button click.
In this section, you have learned how a color is created and stored. Along the way, a dialog that could be used in a number of situations to let that user set that color was created. Once a color was chosen, a Graphics instance used that color to change the color of text on the applet. This, however, is not the only location in which the resulting color can be used. Every component has a foreground color and background color associated with it. The following methods of Component can be used to access these colors.
public Color getBackground(); public void setBackground(Color); public Color getForeground(); public void setForeground(Color);
Because everything that is displayed is a component, these methods can be used to change the color on virtually any item in your applet.
The font selection is encapsulated with the Font class. Since fonts are very system-dependent, an additional class, FontMetrics is used to retrieve specifics about how the font is actually laid out on the output device. This section covers the Font and FontMetrics classes, goes over a dialog used to select a font, and puts that dialog to use in an extension of a previously defined example.
A font can be loaded by specifying a name and size of font desired. This information can be passed to the single Font class constructor:
Font(String, int, int)
This constructor takes a string that contains a name, such as "Dialog" or "Helvetica," an integer containing style indications, and a point size. The style can contain three values defined in the Font class:
PLAIN
BOLD
ITALIC
You can combine these values together by using a bitwise OR operation or by adding them together to form a compound style. Of course, PLAIN combined with anything has no effect (especially since it is defined as 0), but used alone, it indicates that the font will be displayed normally.
After you have created the font, Table 16.2 shows the public methods
that can be used to retrieve some general information about the
font.
| Member | Purpose |
| equals(Object) | Compares two font objects for the same font. |
| GetFamily() | Returns a platform-specific name. |
| getFont(String) | Returns the font of the named system property. |
| getFont(String, Font) | Returns the font of the named system property, returning the font as a default if not found. |
| getName() | Returns the font name. |
| getSize() | Returns the font point size. |
| getStyle() | Returns the font style. |
| hashCode() | Returns a hash code representing the color. |
| isBold() | Returns true if the bold style bit is set. |
| isItalic() | Returns true if the italic style bit is set. |
| isPlain() | Returns true if neither the bold nor italic style bits are set. |
| toString() | Returns a string representation of the font. |
FontMetrics returns specific information about how a particular font is rendered on the output device. This information is typically used so that lines of text are properly spaced.
FontMetrics are based on a given Font. Therefore, the constructor takes the font being examined:
protected FontMetrics(Font)
Notice that the constructor is not public, so a program cannot instantiate an instance of this class. This is done because the information retrieved is very device-specific. Therefore, you must call a device-specific method, such as Component.getFontMetrics() or Graphics.getFontMetrics(), to retrieve the information about the font currently being rendered.
Table 16.3 shows the public methods of the FontMetrics
class.
| Member | Purpose |
| bytesWidth(byte [], int, int) | Returns the width of the array of bytes. |
| charsWidth(char [], int, int) | Returns the width of the array of characters. |
| charWidth(char) | Returns the width of the single character. |
| charWidth(int) | Returns the width of the single character. |
| getAscent() | Returns the standard distance from the baseline of the font to the top of the characters in the font. |
| getDescent() | Returns the standard distance from the baseline of the font to the bottom of the characters in the font. |
| getFont() | Returns the font being analyzed. |
| getHeight() | Returns the total size of a line; sum of leading, ascent, and descent. |
| getLeading() | Returns space required between lines. |
| getMaxAdvance() | Returns the maximum advance. |
| getMaxAscent() | Returns the maximum ascent. |
| getMaxDescent() | Returns the maximum descent. |
| getWidths() | Returns an array of the widths of the first 256 characters. |
| stringWidth(String) | Returns the width of the string. |
You should never have to deal with FontMetrics unless your program is handling the display of text directly to the output devices. By using the components on your applets and dialogs, you let the Java packages deal with these specifics.
Developing a dialog that allows the user to select the font he or she wants to use is an easy way to get familiar with the Font class. Figure 16.3 shows the dialog in action.
Figure 16.3 : Set Font dialog of EX16D.
This dialog contains a choice for the font name and the font size. In addition, the user can select the bold and italic attributes of the font by using the appropriate checkboxes. As a courtesy, a sample of the selected font is displayed. Listing 16.8 shows the dialog class used for the Set Font dialog of example EX16D.
Listing 16.8. SetFontDialog class of EX16D.
class SetFontDialog extends Dialog
{
protected Font selectedFont;
protected FontData fData;
protected Choice NameChoice = new Choice();
protected Choice SizeChoice = new Choice();
protected Checkbox BoldCheckbox = new Checkbox("Bold");
protected Checkbox ItalicCheckbox = new Checkbox("Italic");
protected Label SampleLabel = new Label("AaBbCc", Label.CENTER);
public SetFontDialog(Frame parent, FontData fData)
{
super(parent, "Set Font", true);
setFont(new Font("Dialog", Font.PLAIN, 12));
setBackground(Color.lightGray);
// don't let the user change the size
setResizable(false);
resize(300, 150);
setBackground(Color.lightGray);
// create the controls for the dialog
Panel fontSelectionPanel = new Panel();
fontSelectionPanel.add(NameChoice);
fontSelectionPanel.add(SizeChoice);
fontSelectionPanel.add(BoldCheckbox);
fontSelectionPanel.add(ItalicCheckbox);
add("North", fontSelectionPanel);
Panel fontSamplePanel = new Panel();
fontSamplePanel.add(new Label("Sample"));
fontSamplePanel.add(SampleLabel);
add("Center", fontSamplePanel);
Panel dialogClosePanel = new Panel();
dialogClosePanel.add(new Button("OK"));
dialogClosePanel.add(new Button("Cancel"));
add("South", dialogClosePanel);
// fill in the choice lists
FillChoiceLists();
// save the data and start off with the current font
this.fData = fData;
NameChoice.select(fData.GetFont().getName());
SizeChoice.select(Integer.toString(fData.GetFont().getSize()));
BoldCheckbox.setState(
(fData.GetFont().getStyle() & Font.BOLD) == Font.BOLD);
ItalicCheckbox.setState(
(fData.GetFont().getStyle() & Font.ITALIC) == Font.ITALIC);
GetSelectedFont(false);
SampleLabel.setFont(selectedFont);
}
public boolean action(Event evt, Object arg)
{
boolean result = false; // asume no action
if ("OK".equals(evt.arg)) {
fData.SetFont(selectedFont);
dispose(); // close the dialog
}
if ("Cancel".equals(evt.arg))
dispose(); // close the dialog
return result;
}
public boolean handleEvent(Event evt)
{
boolean result = false; // assume no action
// call the superclass for normal processing
result = super.handleEvent(evt);
// process the event here if not handled yet
if (!result) {
if ((evt.target == NameChoice) ||
(evt.target == SizeChoice) ||
(evt.target == BoldCheckbox) ||
(evt.target == ItalicCheckbox)) {
GetSelectedFont(true);
result = true;
}
}
return result;
}
protected void FillChoiceLists()
{
String fonts[] = Toolkit.getDefaultToolkit().getFontList();
for (int index = 0; index < fonts.length; index++)
NameChoice.addItem(fonts[index]);
// there is no way to get the font sizes that are available
// on the host, so just add some to the listbox
for (int index = 8; index <= 36; index += 4)
SizeChoice.addItem(Integer.toString(index));
}
protected void GetSelectedFont(boolean redisplay)
{
int style = Font.PLAIN;
if (BoldCheckbox.getState())
style |= Font.BOLD;
if (ItalicCheckbox.getState())
style |= Font.ITALIC;
selectedFont = new Font(NameChoice.getSelectedItem(), style,
Integer.parseInt(SizeChoice.getSelectedItem()));
if (redisplay) {
// apply the new font to the sample and make sure
// it fits in the dialog
SampleLabel.setFont(selectedFont);
SampleLabel.resize(SampleLabel.preferredSize());
// tell the layout manager to do its job again
validate();
}
}
}
The class contains several components that will be referenced later within the class to get the selected font and to set up the sample of this selected font. The constructor does some initial setting up of the dialog, including setting the dialog font to a standard font (which is in itself a good example of how to set the font directly) and the color of the background of the dialog to a calm color. Next, the controls are placed on the dialog. The controls are placed in three containers: One holds the selection of the font controls, one holds the sample font controls, and the last holds the controls used to get out of the dialog. All three of these containers are placed on the dialog using the default BorderLayout Manager. Finally, the possible choice values are populated with a call to FillChoiceLists and the controls are initialized with the font passed into the constructor via fData.
action and handleEvent are straightforward and are implemented very similar to the color dialog of EX16B, shown previously.
FillChoiceLists is used to populate the choices. The Toolkit class in java.awt contains a function that will retrieve a list of fonts available to the applet. getFontList was used to retrieve an array of strings containing the font names available. Unfortunately, currently there is not a way to get the font sizes available for each font. Therefore, this example simply puts a variety of numbers into the size choice. This is not a problem because fonts are similar to colors in that if a requested font does not exist, a similar font will be returned in its place.
GetSelectedFont is called to read the font currently selected and to allocate a new Font instance. After a new font has been generated, the sample is updated to use the new font. Because we have the potential of drastically changing the size required to display our sample string, resize is called with the preferredSize of the label. This will change the size of the label to accommodate the new string. Since the layout manager is like most managers and never does anything until told to do so, validate tells the dialog to reposition its controls.
Communication between the Set Font dialog and the applet is once again utilizing the Observer/Observable approach. Listing 16.9 shows the data class used to hold the font information making the trip between the applet and the dialog.
Listing 16.9. FontData class of EX16D.
class FontData extends Observable
{
protected Font fnt = new Font("Dialog", Font.PLAIN, 12);
public Font GetFont()
{
return fnt;
}
public void SetFont(Font fnt)
{
this.fnt = fnt; // save the font
I// flag the change and notify the on-lookers
setChanged();
notifyObservers();
}
}
Using the Set Font dialog is very similar to using the Set Color dialog and the complete example using both dialogs can be found on the accompanying CD as example EX16D. Figure 16.4 shows the resulting applet using one of my favorite colored fonts.
Figure 16.4 : Applet from EX16D.
This chapter covered the elements of an example where a couple of very useful dialogs were created. The first dialog allowed the user to choose a color from either a standard set of colors, or allowed the user to create their own color by selecting the concentrations of red, green, and blue. Once the color was selected, this chapter showed how it was used to change the color of text being drawn in the applet. As an aside, this chapter showed a very useful example of the Observer and Observable utility classes. The second dialog allowed the user to select a font from one of the system fonts. This font was then applied to the text being drawn in the applet.
The next section covers the networking aspects of the Java language. Among other topics, accessing various URLs from within applets, communication using both datagrams and sockets, and Java security support are covered.