ZeePedia

Creating Windows & Applets:Applet restrictions, Running applets from the command line

<< Run-time Type Identification:The need for RTTI, A class method extractor
Multiple Threads:Responsive user interfaces, Sharing limited resources, Runnable revisited >>
img
At compile-time, this is enforcedonly by your ownself-imposed rules,but
at run-time the cast ensuresit.
Nowpolymorphism takes over andthe exact method that'scalled for the
Shapeis determined by whether thereference is for a Circle, Square,
or Triangle. And in general, this is how it should be; you wantthe bulk of
yourcode to know as little as possible about specifictypes of objects, and
to just deal with thegeneral representation of a family of objects (in this
case,Shape). As a result, your code will be easier to write, read,and
maintain,and your designs will be easier to implement, understand,and
change. So polymorphism is the generalgoal in object-oriented
programming.
Butwhat if you have a specialprogramming problem that'seasiest to
solve if you know the exacttype of a generic reference?For example,
supposeyou want to allow yourusers to highlight all theshapes of any
particulartype by turning them purple.This way, they canfind all the
triangles on the screen by highlightingthem. This is whatRTTI
accomplishes:you can ask a Shapereferencethe exact type thatit's
referringto.
The Class object
To understand how RTTI works in Java, you must firstknow how type
information is represented at run-time. This is accomplished through a
specialkind of object called theClassobject, whichcontains information
aboutthe class. (This is sometimes called a meta-class.) In fact, the Class
object is used to create all of the"regular" objects of yourclass.
There's a Classobjectfor each class that is part of your program. Thatis,
eachtime you write andcompile a new class, a single Classobject is also
created(and stored, appropriatelyenough, in an identically named.class
file). At run-time, when you want to make an object of thatclass, the Java
VirtualMachine (JVM) that'sexecuting your program firstchecks to see if
theClassobjectfor that type is loaded. If not, the JVM loads it by finding
the.classfilewith that name. Thus, a Java program isn'tcompletely
loadedbefore it begins, which is different from manytraditional
languages.
662
Thinking in Java
img
Oncethe Classobjectfor that type is in memory, it is used to createall
objects of that type.
If this seems shadowy or if youdon't really believe it,here's a
demonstrationprogram to prove it:
//: c12:SweetShop.java
// Examination of the way the class loader works.
class Candy {
static {
System.out.println("Loading Candy");
}
}
class Gum {
static {
System.out.println("Loading Gum");
}
}
class Cookie {
static {
System.out.println("Loading Cookie");
}
}
public class SweetShop {
public static void main(String[] args) {
System.out.println("inside main");
new Candy();
System.out.println("After creating Candy");
try {
Class.forName("Gum");
} catch(ClassNotFoundException e) {
e.printStackTrace(System.err);
}
System.out.println(
"After Class.forName(\"Gum\")");
new Cookie();
System.out.println("After creating Cookie");
Chapter12: Run time TypeIdentification
663
img
}
} ///:~
Each of the classes Candy, Gum, and Cookiehave a staticclausethat is
executed as the class is loaded forthe first time. Informationwill be
printed to tell you when loadingoccurs for that class. In main( ), the
objectcreations are spread outbetween print statements to help detect
thetime of loading.
A particularly interesting lineis:
Class.forName("Gum");
Thismethod is a staticmember of Class(towhich all Classobjects
belong). A Classobject is like any other objectand so you can getand
manipulate a reference to it. (That'swhat the loader does.)One of the
ways to get a reference to theClassobject is forName( ), which takes a
Stringcontainingthe textual name (watchthe spelling and
capitalization!) of the particular class youwant a reference for. It returns
a Classreference.
Theoutput of this program forone JVM is:
inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie
Youcan see that eachClassobject is loaded only when it'sneeded, and
thestaticinitialization is performed upon classloading.
Classliterals
Javaprovides a second way to produce the reference to theClassobject,
using a classliteral. In theabove program this wouldlook like:
Gum.class;
which is not only simpler, butalso safer since it'schecked at compile-
time.Because it eliminates themethod call, it's alsomore efficient.
664
Thinking in Java
img
Classliterals work with regularclasses as well as interfaces,arrays, and
primitivetypes. In addition, there's a standard field calledTYPE that
existsfor each of the primitivewrapper classes. TheTYPE fieldproduces
a reference to the Classobjectfor the associated primitivetype, such
that:
... is equivalent to ...
boolean.class
Boolean.TYPE
char.class
Character.TYPE
byte.class
Byte.TYPE
short.class
Short.TYPE
int.class
Integer.TYPE
long.class
Long.TYPE
float.class
Float.TYPE
double.class
Double.TYPE
void.class
Void.TYPE
My preference is to use the ".class" versions if you can, sincethey're more
consistentwith regular classes.
Checkingbefore a cast
So far, you've seen RTTIforms including:
1.
Theclassic cast; e.g., "(Shape)," which uses RTTI to makesure the
cast is correct and throws a ClassCastExceptionif you've
performed a bad cast.
2.
TheClassobjectrepresenting the type of your object. The Class
objectcan be queried for usefulrun-time information.
In C++, the classic cast "(Shape)" does not performRTTI. It simply tells
thecompiler to treat the object as the new type. In Java,which does
performthe type check, thiscast is often called a "typesafe downcast."
Chapter12: Run time TypeIdentification
665
img
Thereason for the term"downcast" is the historicalarrangement of the
classhierarchy diagram. If casting a Circleto a Shapeis an upcast, then
casting a Shapeto a Circleis a downcast. However, youknow a Circle
is also a Shape, and the compiler freelyallows an upcast assignment,but
youdon'tknowthat a Shapeis necessarily a Circle, so the compiler
doesn'tallow you to perform a downcast assignment withoutusing an
explicitcast.
There's a third form of RTTI in Java. This is the keywordinstanceofthat
tellsyou if an object is an instance of a particular type. It returns a
booleanso you use it in the form of a question, like this:
if(x instanceof Dog)
((Dog)x).bark();
Theabove if statementchecks to see if the objectx belongs to the class
Dog beforecastingx to a Dog. It's important to useinstanceofbefore a
downcastwhen you don't haveother information that tellsyou the type of
theobject; otherwise you'll end up with a ClassCastException.
Ordinarily,you might be hunting forone type (triangles to turnpurple,
forexample), but you caneasily tally all of the objects using instanceof.
Supposeyou have a family of Petclasses:
//: c12:Pets.java
class Pet {}
class Dog extends Pet {}
class Pug extends Dog {}
class Cat extends Pet {}
class Rodent extends Pet {}
class Gerbil extends Rodent {}
class Hamster extends Rodent {}
class Counter { int i; } ///:~
TheCounterclass is used to keep track of thenumber of anyparticular
type of Pet. You could think of it as an Integerthatcan be modified.
Usinginstanceof, all the pets can be counted:
//: c12:PetCount.java
// Using instanceof.
666
Thinking in Java
img
import java.util.*;
public class PetCount {
static String[] typenames = {
"Pet", "Dog", "Pug", "Cat",
"Rodent", "Gerbil", "Hamster",
};
// Exceptions thrown out to console:
public static void main(String[] args)
throws Exception {
ArrayList pets = new ArrayList();
try {
Class[] petTypes = {
Class.forName("Dog"),
Class.forName("Pug"),
Class.forName("Cat"),
Class.forName("Rodent"),
Class.forName("Gerbil"),
Class.forName("Hamster"),
};
for(int i = 0; i < 15; i++)
pets.add(
petTypes[
(int)(Math.random()*petTypes.length)]
.newInstance());
} catch(InstantiationException e) {
System.err.println("Cannotinstantiate");
throw e;
} catch(IllegalAccessException e) {
System.err.println("Cannot access");
throw e;
} catch(ClassNotFoundException e) {
System.err.println("Cannot find class");
throw e;
}
HashMap h = new HashMap();
for(int i = 0; i < typenames.length; i++)
h.put(typenames[i], new Counter());
for(int i = 0; i < pets.size(); i++) {
Object o = pets.get(i);
if(o instanceof Pet)
Chapter12: Run time TypeIdentification
667
img
((Counter)h.get("Pet")).i++;
if(o instanceof Dog)
((Counter)h.get("Dog")).i++;
if(o instanceof Pug)
((Counter)h.get("Pug")).i++;
if(o instanceof Cat)
((Counter)h.get("Cat")).i++;
if(o instanceof Rodent)
((Counter)h.get("Rodent")).i++;
if(o instanceof Gerbil)
((Counter)h.get("Gerbil")).i++;
if(o instanceof Hamster)
((Counter)h.get("Hamster")).i++;
}
for(int i = 0; i < pets.size(); i++)
System.out.println(pets.get(i).getClass());
for(int i = 0; i < typenames.length; i++)
System.out.println(
typenames[i] + " quantity: " +
((Counter)h.get(typenames[i])).i);
}
} ///:~
There's a rather narrow restriction on instanceof: you can compare it to
a named type only, andnot to a Classobject. In the example aboveyou
mightfeel that it's tedious to write out all of thoseinstanceof
expressions,and you're right. Butthere is no way to cleverlyautomate
instanceofby creating an ArrayList of Classobjectsand comparing it
to those instead (staytuned--you'll see an alternative).This isn't as great
a restriction as you mightthink, because you'lleventually understandthat
yourdesign is probably flawed if you end up writing a lot of instanceof
expressions.
Of course this example is contrived--you'd probably put a staticdata
member in each type and increment it in the constructor to keeptrack of
thecounts. You would do something like thatif youhad control of the
sourcecode for the classand could change it.Since this is not alwaysthe
case,RTTI can come in handy.
668
Thinking in Java
img
Usingclass literals
It'sinteresting to see how thePetCount.javaexamplecan be rewritten
usingclass literals. The result is cleaner in manyways:
//: c12:PetCount2.java
// Using class literals.
import java.util.*;
public class PetCount2 {
public static void main(String[] args)
throws Exception {
ArrayList pets = new ArrayList();
Class[] petTypes = {
// Class literals:
Pet.class,
Dog.class,
Pug.class,
Cat.class,
Rodent.class,
Gerbil.class,
Hamster.class,
};
try {
for(int i = 0; i < 15; i++) {
// Offset by one to eliminate Pet.class:
int rnd = 1 + (int)(
Math.random() * (petTypes.length - 1));
pets.add(
petTypes[rnd].newInstance());
}
} catch(InstantiationException e) {
System.err.println("Cannotinstantiate");
throw e;
} catch(IllegalAccessException e) {
System.err.println("Cannot access");
throw e;
}
HashMap h = new HashMap();
for(int i = 0; i < petTypes.length; i++)
h.put(petTypes[i].toString(),
Chapter12: Run time TypeIdentification
669
img
new Counter());
for(int i = 0; i < pets.size(); i++) {
Object o = pets.get(i);
if(o instanceof Pet)
((Counter)h.get("classPet")).i++;
if(o instanceof Dog)
((Counter)h.get("classDog")).i++;
if(o instanceof Pug)
((Counter)h.get("classPug")).i++;
if(o instanceof Cat)
((Counter)h.get("classCat")).i++;
if(o instanceof Rodent)
((Counter)h.get("classRodent")).i++;
if(o instanceof Gerbil)
((Counter)h.get("classGerbil")).i++;
if(o instanceof Hamster)
((Counter)h.get("classHamster")).i++;
}
for(int i = 0; i < pets.size(); i++)
System.out.println(pets.get(i).getClass());
Iterator keys = h.keySet().iterator();
while(keys.hasNext()) {
String nm = (String)keys.next();
Counter cnt = (Counter)h.get(nm);
System.out.println(
nm.substring(nm.lastIndexOf('.') + 1) +
" quantity: " + cnt.i);
}
}
} ///:~
Here,the typenamesarrayhas been removed in favor of getting the type
namestrings from the Classobject.Notice that the systemcan
distinguishbetween classes andinterfaces.
Youcan also see thatthe creation of petTypesdoesnot need to be
surrounded by a try blocksince it's evaluated at compile-time and thus
won'tthrow any exceptions, unlikeClass.forName( ).
Whenthe Petobjectsare dynamically created, youcan see thatthe
randomnumber is restricted so it is betweenone and petTypes.length
670
Thinking in Java
img
anddoes not include zero.That's because zero refers to Pet.class, and
presumably a generic Petobject is not interesting. However,since
Pet.classis part of petTypestheresult is that all of thepets get counted.
A dynamic instanceof
TheClassisInstance methodprovides a way to dynamicallycall the
instanceofoperator.Thus, all those tediousinstanceofstatementscan
be removed in the PetCountexample:
//: c12:PetCount3.java
// Using isInstance().
import java.util.*;
public class PetCount3 {
public static void main(String[] args)
throws Exception {
ArrayList pets = new ArrayList();
Class[] petTypes = {
Pet.class,
Dog.class,
Pug.class,
Cat.class,
Rodent.class,
Gerbil.class,
Hamster.class,
};
try {
for(int i = 0; i < 15; i++) {
// Offset by one to eliminate Pet.class:
int rnd = 1 + (int)(
Math.random() * (petTypes.length - 1));
pets.add(
petTypes[rnd].newInstance());
}
} catch(InstantiationException e) {
System.err.println("Cannotinstantiate");
throw e;
} catch(IllegalAccessException e) {
System.err.println("Cannot access");
throw e;
Chapter12: Run time TypeIdentification
671
img
}
HashMap h = new HashMap();
for(int i = 0; i < petTypes.length; i++)
h.put(petTypes[i].toString(),
new Counter());
for(int i = 0; i < pets.size(); i++) {
Object o = pets.get(i);
// Using isInstance to eliminate individual
// instanceof expressions:
for (int j = 0; j < petTypes.length; ++j)
if (petTypes[j].isInstance(o)) {
String key = petTypes[j].toString();
((Counter)h.get(key)).i++;
}
}
for(int i = 0; i < pets.size(); i++)
System.out.println(pets.get(i).getClass());
Iterator keys = h.keySet().iterator();
while(keys.hasNext()) {
String nm = (String)keys.next();
Counter cnt = (Counter)h.get(nm);
System.out.println(
nm.substring(nm.lastIndexOf('.') + 1) +
" quantity: " + cnt.i);
}
}
} ///:~
Youcan see that theisInstance( ) methodhas eliminated the needfor
theinstanceofexpressions. In addition, this means thatyou can add
newtypes of pets simply by changing the petTypesarray;the rest of the
programdoes not need modification(as it did when usingthe instanceof
expressions).
instanceof vs.Class equivalence
Whenquerying for typeinformation, there's an importantdifference
betweeneither form of instanceof(thatis, instanceofor
isInstance( ), which produce equivalentresults) and thedirect
comparison of the Classobjects.Here's an example thatdemonstrates
thedifference:
672
Thinking in Java
img
//: c12:FamilyVsExactType.java
// The difference between instanceof and class
class Base {}
class Derived extends Base {}
public class FamilyVsExactType {
static void test(Object x) {
System.out.println("Testing x of type " +
x.getClass());
System.out.println("x instanceof Base " +
(x instanceof Base));
System.out.println("x instanceof Derived " +
(x instanceof Derived));
System.out.println("Base.isInstance(x) " +
Base.class.isInstance(x));
System.out.println("Derived.isInstance(x) " +
Derived.class.isInstance(x));
System.out.println(
"x.getClass() == Base.class " +
(x.getClass() == Base.class));
System.out.println(
"x.getClass() == Derived.class " +
(x.getClass() == Derived.class));
System.out.println(
"x.getClass().equals(Base.class)) " +
(x.getClass().equals(Base.class)));
System.out.println(
"x.getClass().equals(Derived.class)) " +
(x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
} ///:~
Thetest( ) methodperforms type checking withits argument usingboth
forms of instanceof. It then gets the Classreferenceand uses == and
equals( ) to test for equality of theClassobjects.Here is the output:
Testing x of type class Base
Chapter12: Run time TypeIdentification
673
img
x instanceof Base true
x instanceof Derived false
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass() == Base.class true
x.getClass() == Derived.class false
x.getClass().equals(Base.class)) true
x.getClass().equals(Derived.class)) false
Testing x of type class Derived
x instanceof Base true
x instanceof Derived true
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass() == Base.class false
x.getClass() == Derived.class true
x.getClass().equals(Base.class)) false
x.getClass().equals(Derived.class)) true
Reassuringly,instanceofandisInstance( ) produceexactly the same
results, as do equals( ) and==. But the tests themselvesdraw different
conclusions. In keeping with the concept of type, instanceofsays"are
youthis class, or a classderived from this class?" On the other hand, if
youcompare the actual Classobjectsusing ==, there is no concernwith
inheritance--it'seither the exact type or it isn't.
RTTI syntax
Javaperforms its RTTI usingthe Classobject,even if you're doing
somethinglike a cast. The classClassalsohas a number of otherways
youcan use RTTI.
First,you must get a reference to the appropriate Classobject.One way
to do this, as shown in theprevious example, is to use a string and the
Class.forName( ) method.This is convenient becauseyou don't need an
object of that type in order to getthe Classreference.However, if you do
alreadyhave an object of the typeyou're interested in, youcan fetch the
Classreference by calling a method that'spart of the Objectrootclass:
getClass( ). This returns the Classreferencerepresenting theactual
type of the object. Classhasmany interesting methods,demonstrated in
thefollowing example:
674
Thinking in Java
img
//: c12:ToyTest.java
// Testing class Class.
interface HasBatteries {}
interface Waterproof {}
interface ShootsThings {}
class Toy {
// Comment out the following default
// constructor to see
// NoSuchMethodError from (*1*)
Toy() {}
Toy(int i) {}
}
class FancyToy extends Toy
implements HasBatteries,
Waterproof,ShootsThings {
FancyToy() { super(1); }
}
public class ToyTest {
public static void main(String[] args)
throws Exception {
Class c = null;
try {
c = Class.forName("FancyToy");
} catch(ClassNotFoundException e) {
System.err.println("Can't find FancyToy");
throw e;
}
printInfo(c);
Class[] faces = c.getInterfaces();
for(int i = 0; i < faces.length; i++)
printInfo(faces[i]);
Class cy = c.getSuperclass();
Object o = null;
try {
// Requires default constructor:
o = cy.newInstance(); // (*1*)
} catch(InstantiationException e) {
System.err.println("Cannotinstantiate");
Chapter12: Run time TypeIdentification
675
img
throw e;
} catch(IllegalAccessException e) {
System.err.println("Cannot access");
throw e;
}
printInfo(o.getClass());
}
static void printInfo(Class cc) {
System.out.println(
"Class name: " + cc.getName() +
" is interface? [" +
cc.isInterface() + "]");
}
} ///:~
Youcan see that class FancyToy is quitecomplicated, since it inherits
fromToy andimplements theinterfaces of HasBatteries,
Waterproof, and ShootsThings. In main( ), a Classreference is
createdand initialized to theFancyToy Class usingforName( ) inside
an appropriate try block.
TheClass.getInterfaces( ) methodreturns an array of Classobjects
representingthe interfaces that arecontained in the Classobject of
interest.
If you have a Classobjectyou can also ask it for its direct baseclass using
getSuperclass( ). This, of course, returns a Classreferencethat you can
furtherquery. This means that, at run-time, you can discover an object's
entireclass hierarchy.
ThenewInstance( ) method of Classcan, at first, seem like justanother
way to clone( ) an object. However, you cancreate a new objectwith
newInstance( ) without an existing object, as seenhere, because there
is no Toy object--onlycy, which is a reference to y's Classobject.This is
a way to implement a "virtualconstructor," which allowsyou to say "I
don'tknow exactly what typeyou are, but createyourself properly
anyway." In the example above,cy is just a Classreferencewith no
furthertype information known at compile-time. And when youcreate a
newinstance, you get back an Objectreference. Butthat reference is
pointing to a Toy object. Of course, before you cansend any messages
otherthan those accepted by Object, you have to investigate it a bit and
676
Thinking in Java
img
do some casting. In addition,the class that's beingcreated with
newInstance( ) musthave a default constructor. In the next section,
you'llsee how to dynamicallycreate objects of classesusing any
constructor,with the Java reflectionAPI.
Thefinal method in the listing is printInfo( ), whichtakes a Class
referenceand gets its namewith getName( ), andfinds out whetherit's
an interface with isInterface( ).
Theoutput from this programis:
Class
name:
FancyToy is interface? [false]
Class
name:
HasBatteries is interface? [true]
Class
name:
Waterproof is interface? [true]
Class
name:
ShootsThings is interface? [true]
Class
name:
Toy is interface? [false]
Thus,with the Classobjectyou can find outjust about everythingyou
want to know about an object.
Reflection: run-time
class information
If you don't know theprecise type of an object,RTTI will tellyou.
However,there's a limitation: thetype must be known at compile-time in
orderfor you to be able to detect it using RTTI and do something useful
withthe information. Put anotherway, the compiler mustknow about all
theclasses you're working withfor RTTI.
Thisdoesn't seem like thatmuch of a limitation at first,but suppose
you'regiven a reference to an objectthat's not in your programspace. In
fact,the class of the objectisn't even available to yourprogram at
compile-time.For example, suppose youget a bunch of bytes from a disk
file or from a network connectionand you're told thatthose bytes
represent a class. Since the compilercan't know about theclass while it's
compilingthe code, how canyou possibly use such a class?
In a traditional programming environmentthis seems like a far-fetched
scenario.But as we move into a largerprogramming world thereare
Chapter12: Run time TypeIdentification
677
img
importantcases in which this happens.The first is component-based
programming, in which you build projectsusing RapidApplication
Development(RAD) in an application builder tool.This is a visual
approach to creating a program (whichyou see on the screen as a "form")
by moving icons that representcomponents onto the form.These
componentsare then configured by setting some of their values at
programtime. This design-timeconfiguration requires thatany
component be instantiable, that it exposesparts of itself, and that it allows
itsvalues to be read and set. In addition, components thathandle GUI
eventsmust expose informationabout appropriate methods so that the
RADenvironment can assist theprogrammer in overriding theseevent-
handlingmethods. Reflection providesthe mechanism to detectthe
availablemethods and produce themethod names. Java provides a
structurefor component-based programmingthrough JavaBeans
(described in Chapter 13).
Anothercompelling motivation fordiscovering class information at run-
time is to provide the ability to create and execute objects on remote
platformsacross a network. This is called RemoteMethod Invocation
(RMI)and it allows a Java program to have objects distributedacross
manymachines. This distributioncan happen for a number of reasons:
forexample, perhaps you'redoing a computation-intensive taskand you
want to break it up and putpieces on machines that areidle in order to
speedthings up. In somesituations you might want to place code that
handlesparticular types of tasks(e.g., "Business Rules" in a multitier
client/serverarchitecture) on a particular machine, so that machine
becomes a common repository describingthose actions and it can be
easilychanged to affect everyone in the system. (This is an interesting
development,since the machine existssolely to make softwarechanges
easy!)Along these lines,distributed computing alsosupports specialized
hardwarethat might be good at a particular task--matrix inversions,for
example--butinappropriate or too expensivefor general purpose
programming.
Theclass Class(describedpreviously in this chapter)supports the
concept of reflection, and there's an additionallibrary,
java.lang.reflect,withclasses Field, Method, and Constructor
(each of which implement theMember interface). Objects of these
typesare created by the JVM at run-time to represent thecorresponding
678
Thinking in Java
img
member in the unknown class. Youcan then use theConstructors to
createnew objects, the get( ) andset( ) methods to read and modifythe
fieldsassociated with Fieldobjects,and the invoke( ) method to call a
methodassociated with a Method object. In addition, you can callthe
conveniencemethods getFields( ), getMethods( ),
getConstructors( ), etc., to return arrays of the objects representingthe
fields,methods, and constructors.(You can find outmore by looking up
theclass Classin your online documentation.)Thus, the class
informationfor anonymous objects can be completely determined at run-
time,and nothing need be known at compile-time.
It'simportant to realize thatthere's nothing magic aboutreflection. When
you'reusing reflection to interactwith an object of an unknowntype, the
JVMwill simply look at theobject and see that it belongs to a particular
class(just like ordinary RTTI)but then, before it can do anything else, the
Classobjectmust be loaded. Thus, the.classfilefor that particulartype
muststill be available to theJVM, either on the localmachine or across
thenetwork. So the truedifference between RTTI andreflection is that
withRTTI, the compiler opensand examines the .classfile at compile-
time.Put another way, youcan call all themethods of an object in the
"normal"way. With reflection, the.classfile is unavailable at compile-
time; it is opened and examined by the run-timeenvironment.
A class method extractor
You'llrarely need to use thereflection tools directly;they're in the
language to support other Javafeatures, such as objectserialization
(Chapter11), JavaBeans (Chapter 13),and RMI (Chapter 15).However,
thereare times when it'squite useful to be able to dynamically extract
informationabout a class. One extremelyuseful tool is a classmethod
extractor. As mentioned before, looking at a class definition sourcecode
or online documentation showsonly the methods thatare defined or
overriddenwithin that class definition. But there could be dozensmore
available to you that have comefrom base classes. To locatethese is both
tediousand time consuming1. Fortunately, reflection provides a way to
1 Especially in the past.However, Sun has greatlyimproved its HTML Javadocumentation
so that it's easier to seebase-class methods.
Chapter12: Run time TypeIdentification
679
img
write a simple tool that willautomatically show you theentire interface.
Here'sthe way it works:
//: c12:ShowMethods.java
// Using reflection to show all the methods of
// a class, even if the methods are defined in
// the base class.
import java.lang.reflect.*;
public class ShowMethods {
static final String usage =
"usage: \n" +
"ShowMethodsqualified.class.name\n" +
"To show all methods in class or: \n" +
"ShowMethodsqualified.class.name word\n" +
"To search for methods involving 'word'";
public static void main(String[] args) {
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
try {
Class c = Class.forName(args[0]);
Method[] m = c.getMethods();
Constructor[] ctor = c.getConstructors();
if(args.length == 1) {
for (int i = 0; i < m.length; i++)
System.out.println(m[i]);
for (int i = 0; i < ctor.length; i++)
System.out.println(ctor[i]);
} else {
for (int i = 0; i < m.length; i++)
if(m[i].toString()
.indexOf(args[1])!= -1)
System.out.println(m[i]);
for (int i = 0; i < ctor.length; i++)
if(ctor[i].toString()
.indexOf(args[1])!= -1)
System.out.println(ctor[i]);
}
} catch(ClassNotFoundException e) {
680
Thinking in Java
img
System.err.println("No such class: " + e);
}
}
} ///:~
TheClassmethodsgetMethods( ) andgetConstructors( ) return an
array of Method andConstructor, respectively. Each of theseclasses
hasfurther methods to dissectthe names, arguments, andreturn values
of the methods they represent.But you can alsojust use toString( ), as is
donehere, to produce a Stringwiththe entire method signature.The rest
of the code is just forextracting command lineinformation, determining
if a particular signature matcheswith your target string(using
indexOf( )), and printing theresults.
Thisshows reflection in action,since the result produced by
Class.forName( ) cannot be known at compile-time, andtherefore all
themethod signature information is being extracted at run-time. If you
investigateyour online documentation on reflection, you'll see thatthere
is enough support to actuallyset up and make a methodcall on an object
that'stotally unknown at compile-time(there will be examples of this
later in this book). Again, this is something you may neverneed to do
yourself--thesupport is there for RMIand so a programming
environmentcan manipulate JavaBeans--butit's interesting.
An interesting experiment is to run
java ShowMethods ShowMethods
Thisproduces a listing thatincludes a publicdefaultconstructor, even
thoughyou can see fromthe code that no constructorwas defined. The
constructoryou see is the onethat's automatically synthesized by the
compiler. If you then make ShowMethodsa non-publicclass(that is,
friendly),the synthesized defaultconstructor no longer shows up in the
output.The synthesized defaultconstructor is automatically giventhe
sameaccess as the class.
Theoutput for ShowMethodsis still a little tedious. Forexample, here's
a portion of the outputproduced by invoking javaShowMethods
java.lang.String:
public boolean
Chapter12: Run time TypeIdentification
681
img
java.lang.String.startsWith(java.lang.String,int)
public boolean
java.lang.String.startsWith(java.lang.String)
public boolean
java.lang.String.endsWith(java.lang.String)
It would be even nicer if thequalifiers like java.langcould be stripped
off.The StreamTokenizerclassintroduced in the previouschapter can
helpcreate a tool to solve thisproblem:
//: com:bruceeckel:util:StripQualifiers.java
package com.bruceeckel.util;
import java.io.*;
public class StripQualifiers {
private StreamTokenizer st;
public StripQualifiers(String qualified) {
st = new StreamTokenizer(
new StringReader(qualified));
st.ordinaryChar(' '); // Keep the spaces
}
public String getNext() {
String s = null;
try {
int token = st.nextToken();
if(token != StreamTokenizer.TT_EOF) {
switch(st.ttype) {
case StreamTokenizer.TT_EOL:
s = null;
break;
case StreamTokenizer.TT_NUMBER:
s = Double.toString(st.nval);
break;
case StreamTokenizer.TT_WORD:
s = new String(st.sval);
break;
default: // single character in ttype
s = String.valueOf((char)st.ttype);
}
}
} catch(IOException e) {
682
Thinking in Java
img
System.err.println("Error fetching token");
}
return s;
}
public static String strip(String qualified) {
StripQualifiers sq =
new StripQualifiers(qualified);
String s = "", si;
while((si = sq.getNext()) != null) {
int lastDot = si.lastIndexOf('.');
if(lastDot != -1)
si = si.substring(lastDot + 1);
s += si;
}
return s;
}
} ///:~
To facilitate reuse, this class is placed in com.bruceeckel.util. As you
cansee, this uses theStreamTokenizerandStringmanipulation to do
itswork.
Thenew version of the programuses the above class to clean up the
output:
//: c12:ShowMethodsClean.java
// ShowMethods with the qualifiers stripped
// to make the results easier to read.
import java.lang.reflect.*;
import com.bruceeckel.util.*;
public class ShowMethodsClean {
static final String usage =
"usage: \n" +
"ShowMethodsCleanqualified.class.name\n" +
"To show all methods in class or: \n" +
"ShowMethodsCleanqualif.class.name word\n" +
"To search for methods involving 'word'";
public static void main(String[] args) {
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
Chapter12: Run time TypeIdentification
683
img
}
try {
Class c = Class.forName(args[0]);
Method[] m = c.getMethods();
Constructor[] ctor = c.getConstructors();
// Convert to an array of cleaned Strings:
String[] n =
new String[m.length + ctor.length];
for(int i = 0; i < m.length; i++) {
String s = m[i].toString();
n[i] = StripQualifiers.strip(s);
}
for(int i = 0; i < ctor.length; i++) {
String s = ctor[i].toString();
n[i + m.length] =
StripQualifiers.strip(s);
}
if(args.length == 1)
for (int i = 0; i < n.length; i++)
System.out.println(n[i]);
else
for (int i = 0; i < n.length; i++)
if(n[i].indexOf(args[1])!= -1)
System.out.println(n[i]);
} catch(ClassNotFoundException e) {
System.err.println("No such class: " + e);
}
}
} ///:~
Theclass ShowMethodsCleanis quite similar to theprevious
ShowMethods, except that it takes thearrays of Method and
Constructorandconverts them into a singlearray of String. Each of
theseStringobjects is then passed throughStripQualifiers.Strip( ) to
removeall the methodqualification.
Thistool can be a realtime-saver while you'reprogramming, when you
can'tremember if a class has a particular method and youdon't want to
go walking through the classhierarchy in the onlinedocumentation, or if
youdon't know whether thatclass can do anything with,for example,
Colorobjects.
684
Thinking in Java
img
Chapter 13 contains a GUI version of thisprogram (customized to extract
informationfor Swing components) so youcan leave it runningwhile
you'rewriting code, to allow quicklookups.
Summary
RTTIallows you to discover typeinformation from an anonymousbase-
classreference. Thus, it's ripefor misuse by the novicesince it might
makesense before polymorphicmethod calls do. Formany people coming
from a procedural background, it'sdifficult not to organizetheir programs
intosets of switchstatements.They could accomplish thiswith RTTI and
thuslose the important value of polymorphism in code developmentand
maintenance.The intent of Java is thatyou use polymorphic methodcalls
throughoutyour code, and youuse RTTI only whenyou must.
However,using polymorphic methodcalls as they are intendedrequires
thatyou have control of thebase-class definition because at some point in
theextension of your programyou might discover thatthe base class
doesn'tinclude the method youneed. If the base classcomes from a
library or is otherwise controlled by someoneelse, a solution to the
problem is RTTI: You can inherit a new type and addyour extra method.
Elsewhere in the code you candetect your particular typeand call that
specialmethod. This doesn't destroythe polymorphism andextensibility
of the program because adding a new type will notrequire you to huntfor
switchstatements in your program.However, when you addnew code in
yourmain body that requiresyour new feature, youmust use RTTI to
detectyour particular type.
Putting a feature in a base classmight mean that, forthe benefit of one
particularclass, all of the otherclasses derived from thatbase require
somemeaningless stub of a method.This makes the interfaceless clear
andannoys those who mustoverride abstract methodswhen they derive
fromthat base class. Forexample, consider a classhierarchy representing
musicalinstruments. Suppose youwanted to clear the spitvalves of all the
appropriateinstruments in your orchestra.One option is to put a
clearSpitValve( ) method in the base class Instrument, but this is
confusingbecause it implies thatPercussionandElectronic
instrumentsalso have spit valves.RTTI provides a much morereasonable
Chapter12: Run time TypeIdentification
685
img
solution in this case because youcan place the method in the specific class
(Wind in this case), where it'sappropriate. However, a moreappropriate
solution is to put a prepareInstrument( ) method in the base class,but
youmight not see thiswhen you're first solvingthe problem andcould
mistakenlyassume that you mustuse RTTI.
Finally,RTTI will sometimes solveefficiency problems. If yourcode nicely
usespolymorphism, but it turnsout that one of yourobjects reacts to this
generalpurpose code in a horriblyinefficient way, you canpick out that
typeusing RTTI and writecase-specific code to improvethe efficiency. Be
wary,however, of programming forefficiency too soon. It's a seductive
trap.It's best to get theprogram working first, then decide if it'srunning
fastenough, and only thenshould you attack efficiencyissues--with a
profiler.
Exercises
Solutions to selected exercises can be found in the electronicdocument TheThinking in Java
AnnotatedSolution Guide, availablefor a small fee from.
1.
AddRhomboid to Shapes.java. Create a Rhomboid, upcast it
to a Shape, then downcast it back to a Rhomboid. Try
downcasting to a Circleandsee what happens.
2.
ModifyExercise 1 so that it usesinstanceofto check the type
beforeperforming thedowncast.
3.
ModifyShapes.javaso that it can "highlight" (set a flag) in all
shapes of a particular type. ThetoString( ) methodfor each
derivedShapeshouldindicate whether thatShapeis
"highlighted."
4.
ModifySweetShop.javaso that each type of objectcreation is
controlled by a command-line argument. Thatis, if your command
line is "javaSweetShop Candy," thenonly the Candy object is
created.Notice how you cancontrol which Classobjectsare
loadedvia the command-lineargument.
5.
Add a new type of Petto PetCount3.java. Verify that it is
createdand counted correctly in main( ).
686
Thinking in Java
img
6.
Write a method that takes an object and recursivelyprints all the
classes in that object'shierarchy.
7.
ModifyExercise 6 so that it usesClass.getDeclaredFields( ) to
alsodisplay information aboutthe fields in a class.
8.
In ToyTest.java, comment out Toy's default constructorand
explainwhat happens.
9.
Incorporate a new kind of interfaceintoToyTest.javaand
verifythat it is detected anddisplayed properly.
10.
Create a new type of container thatuses a private ArrayList to
holdthe objects. Capture thetype of the first objectyou put in it,
andthen allow the user to insert objects of only thattype from
thenon.
11.
Write a program to determine whether an array of char is a
primitivetype or a trueobject.
12.
ImplementclearSpitValve( ) as described in thesummary.
13.
Implementthe rotate(Shape)methoddescribed in thischapter,
suchthat it checks to see if it is rotating a Circle(and, if so,
doesn'tperform theoperation).
14.
ModifyExercise 6 so that it usesreflection instead of RTTI.
15.
ModifyExercise 7 so that it usesreflection instead of RTTI.
16.
In ToyTest.java, use reflection to create a Toy objectusing the
nondefaultconstructor.
17.
Look up the interface forjava.lang.Classin the HTML Java
documentationfrom java.sun.com. Write a program that takesthe
name of a class as a command-line argument,then uses the Class
methods to dump all the informationavailable for that class.Test
yourprogram with a standardlibrary class and a classyou create.
Chapter12: Run time TypeIdentification
687
img
13: Creating
Windows
& Applets
A fundamental design guideline is "make simple things
easy,and difficult thingspossible."  1
Theoriginal design goal of thegraphical user interface(GUI) library in
Java1.0 was to allow theprogrammer to build a GUI thatlooks good on
allplatforms. That goal wasnot achieved. Instead, theJava 1.0 Abstract
Window Toolkit (AWT)produces a GUI that looksequally mediocre on all
systems. In addition, it's restrictive:you can use onlyfour fonts andyou
cannotaccess any of the moresophisticated GUI elements thatexist in
youroperating system. The Java1.0 AWT programming model is also
awkwardand non-object-oriented. A student in one of my seminars(who
hadbeen at Sun during thecreation of Java) explainedwhy: the original
AWThad been conceptualized,designed, and implemented in a month.
Certainly a marvel of productivity, andalso an object lesson in whydesign
is important.
Thesituation improved with theJava 1.1 AWT eventmodel, which takes a
muchclearer, object-oriented approach,along with the addition of
JavaBeans, a component programming modelthat is oriented towardthe
easycreation of visual programmingenvironments. Java 2 finishesthe
transformationaway from the oldJava 1.0 AWT by essentiallyreplacing
everythingwith the JavaFoundation Classes (JFC),the GUI portion of
which is called "Swing." These are a rich set of easy-to-use,easy-to-
1 A variation on this is called "theprinciple of least astonishment," which essentially says:
"don'tsurprise the user."
689
img
understandJavaBeans that can be dragged and dropped (aswell as hand
programmed) to create a GUI that you can(finally) be satisfied with.The
"revision 3" rule of the softwareindustry (a product isn'tgood until
revision 3) seems to hold true withprogramming languages as well.
Thischapter does not coveranything but the modern,Java 2 Swing
library,and makes the reasonableassumption that Swing is thefinal
destination GUI library for Java. If forsome reason you need to use the
original"old" AWT (because you'resupporting old code or youhave
browserlimitations), you can findthat introduction in thefirst edition of
thisbook, downloadable at (alsoincluded on the
CD ROM bound with thisbook).
Early in this chapter, you'll seehow things are differentwhen you want to
create an applet vs. a regularapplication using Swing, andhow to create
programsthat are both appletsand applications so they can be run either
inside a browser or from thecommand line. Almost allthe GUI examples
in this book will be executable as either applets or applications.
Please be aware that this is not a comprehensive glossary of eitherall the
Swingcomponents, or all themethods for the describedclasses. What you
seehere is intended to be simple.The Swing library is vast,and the goal of
thischapter is only to get youstarted with the essentialsand comfortable
withthe concepts. If you need to do more, then Swing canprobably give
youwhat you want if you'rewilling to do theresearch.
I assume here that youhave downloaded andinstalled the (free)Java
librarydocuments in HTML formatfrom java.sun.comandwill browse
thejavax.swingclasses in that documentation to seethe full detailsand
methods of the Swing library.Because of the simplicity of the Swing
design,this will often be enoughinformation to solve yourproblem. There
arenumerous (rather thick)books dedicated solely to Swing and you'll
want to go to those if you needmore depth, or if you want to modify the
defaultSwing behavior.
As you learn about Swingyou'll discover:
1.
Swing is a much better programmingmodel than you'veprobably
seen in other languages anddevelopment environments.
690
Thinking in Java
img
JavaBeans(which will be introducedtoward the end of this
chapter) is the framework for thatlibrary.
2.
"GUIbuilders" (visual programmingenvironments) are a de
rigueuraspect of a complete Java developmentenvironment.
JavaBeansand Swing allow the GUI builder to write code foryou as
youplace components onto formsusing graphical tools. Thisnot
onlyrapidly speeds developmentduring GUI building, but it allows
forgreater experimentation andthus the ability to tryout more
designsand presumably come up with a better one.
3.
Thesimplicity and well-designednature of Swing means thateven
if you do use a GUI builderrather than coding by hand,the
resultingcode will still be comprehensible--this solves a big
problemwith GUI builders from thepast, which couldeasily
generateunreadable code.
Swingcontains all the componentsthat you expect to see in a modern UI,
everythingfrom buttons that containpictures to trees andtables. It's a big
library,but it's designed to haveappropriate complexity forthe task at
hand--ifsomething is simple, youdon't have to write muchcode but as
youtry to do more complexthings, your code becomesproportionally
morecomplex. This means an easyentry point, but you'vegot the power if
youneed it.
Much of what you'll like aboutSwing could be called"orthogonality of
use."That is, once youpick up the general ideasabout the library youcan
applythem everywhere. Primarilybecause of the standardnaming
conventions,much of the time that I was writing these examples I could
guess at the method names andget it right the firsttime, without looking
anythingup. This is certainly thehallmark of a good librarydesign. In
addition,you can generally plugcomponents into othercomponents and
thingswill work correctly.
Forspeed, all the componentsare "lightweight," and Swing is written
entirely in Java forportability.
Keyboardnavigation is automatic--you canrun a Swingapplication
withoutusing the mouse, andthis doesn't require anyextra
programming.Scrolling support is effortless--yousimply wrap your
Chapter13: Creating Windows & Applets
691
img
component in a JScrollPaneas you add it to your form.Features such as
tooltips typically require a single line of code to use.
Swingalso supports a ratherradical feature called"pluggable look and
feel,"which means that theappearance of the UI can be dynamically
changed to suit the expectations of users working underdifferent
platformsand operating systems. It'seven possible (albeitdifficult) to
inventyour own look andfeel.
The basic applet
One of Java's design goals is to create applets, which are littleprograms
thatrun inside a Web browser.Because they must be safe,applets are
limited in what they can accomplish.However, applets are a powerful tool
thatsupport client-side programming, a major issue for theWeb.
Appletrestrictions
Programmingwithin an applet is so restrictivethat it's often referred to as
being"inside the sandbox," sinceyou always havesomeone--that is, the
Javarun-time security system--watchingover you.
However,you can also stepoutside the sandbox andwrite regular
applicationsrather than applets, in which case you canaccess the other
features of your OS. We've beenwriting regular applicationsall along in
thisbook, but they've beenconsoleapplications withoutany graphical
components.Swing can also be used to build GUI interfaces forregular
applications.
Youcan generally answer thequestion of what an applet is able to do by
looking at what it is supposedto do: extend the functionality of a Web
page in a browser. Since, as a Netsurfer, you never reallyknow if a Web
page is from a friendly place or not, you want anycode that it runs to be
safe. So the biggest restrictionsyou'll notice areprobably:
1.
An applet can't touch thelocal disk. Thismeans writing or reading,
sinceyou wouldn't want an applet to read and transmitprivate
informationover the Internet withoutyour permission. Writing is
prevented, of course, since that would be an open invitation to a
virus.Java offers digitalsigning forapplets. Many applet
692
Thinking in Java
img
restrictionsare relaxed when youchoose to allow trustedapplets
(thosesigned by a trusted source) to have access to yourmachine.
2.
Appletscan take longer to display,sinceyou must downloadthe
wholething every time, including a separate server hit foreach
differentclass. Your browser cancache the applet, butthere are no
guarantees.Because of this, you shouldalways package your
applets in a JAR (Java ARchive) filethat combines all theapplet
components(including other .classfiles as well as images and
sounds)together into a singlecompressed file that can be
downloaded in a single server transaction."Digital signing" is
availablefor each individual entry in the JAR file.
Appletadvantages
If you can live withinthe restrictions, appletshave definiteadvantages,
especiallywhen building client/server or other networkedapplications:
1.
There is no installation issue. An applethas true platform
independence(including the ability to easily play audio files,etc.)
so you don't need to makeany changes in your codefor different
platformsnor does anyone have to perform any "tweaking" on
installation. In fact, installation is automaticevery time theuser
loadsthe Web page thatcontains applets, so updateshappen
silentlyand automatically. In traditionalclient/server systems,
buildingand installing a new version of the client software is often
a nightmare.
2.
Youdon't have to worry about bad code causing damage to
someone'ssystem, because of the security built intothe core Java
languageand applet structure. This,along with the previouspoint,
makesJava popular for so-calledintranetclient/server
applicationsthat live only within a company or restricted arena of
operationwhere the user environment(Web browser andadd-ins)
can be specified and/orcontrolled.
Becauseapplets are automaticallyintegrated with HTML, youhave a
built-inplatform-independent documentation system to support the
applet.It's an interesting twist,since we're used to havingthe
documentationpart of the program ratherthan vice versa.
Chapter13: Creating Windows & Applets
693
img
Applicationframeworks
Librariesare often grouped according to their functionality.Some
libraries,for example, are used as is, off the shelf.The standard Java
libraryStringandArrayList classesare examples of these.Other
librariesare designed specifically as building blocks to createother
classes. A certain category of library is the applicationframework, whose
goal is to help you buildapplications by providing a class or set of classes
thatproduces the basic behaviorthat you need in everyapplication of a
particulartype. Then, to customize thebehavior to your own needs,you
inheritfrom the application classand override the methods of interest.
Theapplication framework's defaultcontrol mechanism will callyour
overriddenmethods at the appropriatetime. An application framework is
a good example of "separatingthe things that changefrom the thingsthat
staythe same," since it attempts to localize all the uniqueparts of a
program in the overriddenmethods2.
Appletsare built using an application framework. Youinherit from class
JApplet andoverride the appropriatemethods. There are a fewmethods
thatcontrol the creation andexecution of an applet on a Webpage:
Method
Operation
init( )
Automaticallycalled to perform first-timeinitialization
of the applet, includingcomponent layout. You'llalways
overridethis method.
start( )
Calledevery time the appletmoves into sight on the
Webbrowser to allow the applet to start up itsnormal
operations(especially those that areshut off by
stop( )). Also called after init( ).
stop( )
Calledevery time the appletmoves out of sight on the
Webbrowser to allow the applet to shut off expensive
operations.Also called right beforedestroy( ).
destroy( )
Calledwhen the applet is beingunloaded from thepage
to perform final release of resources when the applet is
no longer used
2 This is an example of the design pattern called the templatemethod.
694
Thinking in Java
img
Withthis information you areready to create a simpleapplet:
//: c13:Applet1.java
// Very simple applet.
import javax.swing.*;
import java.awt.*;
public class Applet1 extends JApplet {
public void init() {
getContentPane().add(newJLabel("Applet!"));
}
} ///:~
Notethat applets are notrequired to have a main( ). That's all wiredinto
theapplication framework; youput any startup code in init( ).
In this program, the onlyactivity is putting a textlabel on the applet,via
theJLabelclass(the old AWT appropriatedthe name Labelas well as
othernames of components, so youwill often see a leading "J" used with
Swingcomponents). The constructorfor this class takes a Stringand
uses it to create the label. In the above program thislabel is placed on the
form.
Theinit( ) method is responsible for puttingall the components on the
formusing the add( ) method.You might think thatyou ought to be able
to simply call add( ) by itself, and in fact that'sthe way it used to be in the
oldAWT. However, Swing requiresyou to add all components to the
"contentpane" of a form, and so youmust call getContentPane( ) as
part of the add( ) process.
Running applets inside a Web
browser
To run this program youmust place it inside a Webpage and viewthat
pageinside your Java-enabled Webbrowser. To place an appletinside a
Chapter13: Creating Windows & Applets
695
img
Webpage you put a specialtag inside the HTMLsource for thatWeb
page3 to tell the page how to load and run theapplet.
Thisprocess used to be verysimple, when Java itselfwas simple and
everyonewas on the same bandwagonand incorporated the sameJava
supportinside their Web browsers.Then you might havebeen able to get
awaywith a very simple bit of HTML inside your Webpage, like this:
<applet code=Applet1 width=100 height=50>
</applet>
Thenalong came the browserand language wars, and we (programmers
andend users alike) lost.After awhile, JavaSoftrealized that we could no
longerexpect browsers to supportthe correct flavor of Java,and the only
solutionwas to provide some kind of add-on that would conform to a
browser'sextension mechanism. By usingthe extensionmechanism
(which a browser vendor cannotdisable--in an attempt to gain
competitiveadvantage--without breaking allthe third-partyextensions),
JavaSoftguarantees that Java cannot be shut out of the Webbrowser by
an antagonistic vendor.
WithInternet Explorer, theextension mechanism is theActiveX control,
andwith Netscape it is theplug-in. In your HTML code,you must provide
tags to support both. Here's whatthe simplest resulting HTMLpage looks
likefor Applet1:4
//:! c13:Applet1.html
<html><head><title>Applet1</title></head><hr>
<OBJECT
classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
width="100"height="50" align="baseline"
codebase="http://java.sun.com/products/plugin/1.2.2/ji
nstall-1_2_2-win.cab#Version=1,2,2,0">
<PARAM NAME="code"VALUE="Applet1.class">
3 It is assumedthat the reader is familiarwith the basics of HTML.It's not too hard to
figure out, and there are lots of booksand resources.
4 This page--inparticular, the `clsid' portion--seemed to work fine with both JDK1.2.2 and
JDK1.3 rc-1. However, you mayfind that you have to change the tag sometime in the
future.Details can be found at java.sun.com.
696
Thinking in Java
img
<PARAM NAME="codebase" VALUE=".">
<PARAM NAME="type"VALUE="application/x-java-
applet;version=1.2.2">
<COMMENT>
<EMBED type=
"application/x-java-applet;version=1.2.2"
width="200"height="200" align="baseline"
code="Applet1.class"codebase="."
pluginspage="http://java.sun.com/products/plugin/1.2/p
lugin-install.html">
<NOEMBED>
</COMMENT>
No Java 2 support for APPLET!!
</NOEMBED>
</EMBED>
</OBJECT>
<hr></body></html>
///:~
Some of these lines were toolong and had to be wrapped to fit on the
page.The code in this book'ssource code (on thebook's CD ROM, and
downloadablefrom ) will work without having to
worryabout correcting linewraps.
Thecodevaluegives the name of the.classfilewhere the appletresides.
Thewidth andheightspecifythe initial size of theapplet (in pixels, as
before).There are other itemsyou can place withinthe applet tag: a place
to find other .classfiles on the Internet (codebase), alignment
information (align), a special identifier thatmakes it possible forapplets
to communicate with each other (name), and applet parameters to
provideinformation that the appletcan retrieve. Parameters are in the
form:
<param name="identifier" value = "information">
andthere can be as many as youwant.
Thesource code package forthis book provides an HTMLpage for each of
theapplets in this book, andthus many examples of theapplet tag. You
canfind a full and currentdescription of the details of placing applets in
Webpages at java.sun.com.
Chapter13: Creating Windows & Applets
697
img
Using Appletviewer
Sun'sJDK (freely downloadablefrom java.sun.com) contains a toolcalled
theAppletviewerthatpicks the <applet> tagsout of the HTML fileand
runsthe applets withoutdisplaying the surroundingHTML text. Because
theAppletviewer ignores everythingbut APPLET tags, youcan put those
tags in the Java source file as comments:
// < applet code=MyApplet width=200 height=100>
// < /applet>
Thisway, you can run "appletviewerMyApplet.java" andyou don't
need to create tiny HTML files to run tests. For example,you can addthe
commentedHTML tags to Applet1.java:
//: c13:Applet1b.java
// Embedding the applet tag for Appletviewer.
// < applet code=Applet1b width=100 height=50>
// < /applet>
import javax.swing.*;
import java.awt.*;
public class Applet1b extends JApplet {
public void init() {
getContentPane().add(newJLabel("Applet!"));
}
} ///:~
Nowyou can invoke theapplet with thecommand
appletviewerApplet1b.java
In this book, this formwill be used for easytesting of applets.Shortly,
you'llsee another coding approachwhich will allow you to execute applets
fromthe command line withoutthe Appletviewer.
Testingapplets
Youcan perform a simple testwithout any networkconnection by starting
up your Web browser andopening the HTML filecontaining the applet
tag. As the HTML file is loaded,the browser will discoverthe applet tag
and go hunt for the .classfilespecified by the codevalue. Of course, it
698
Thinking in Java
img
looks at the CLASSPATH to find outwhere to hunt, and if your.classfile
isn't in the CLASSPATH then it will give an error message on the status
line of the browser to the effectthat it couldn't find that.classfile.
Whenyou want to try thisout on your Web sitethings are a littlemore
complicated.First of all, you musthavea Web site, which formost people
means a third-party Internet ServiceProvider (ISP) at a remotelocation.
Sincethe applet is just a file or set of files, the ISPdoes not have to
provideany special support forJava. You must alsohave a way to move
theHTML files and the.classfilesfrom your site to thecorrect directory
on the ISP machine. This is typically done with a FileTransfer Protocol
(FTP)program, of which there aremany different typesavailable for free
or as shareware. So it would seemthat all you need to do is move the files
to the ISP machine withFTP, then connect to thesite and HTMLfile
usingyour browser; if the appletcomes up and works, theneverything
checksout, right?
Here'swhere you can getfooled. If the browser on the client machine
cannotlocate the .classfile on the server, it will huntthrough the
CLASSPATH on your localmachine.Thus, the applet mightnot be
loadingproperly from the server,but to you it looks fineduring your
testingprocess because the browserfinds it on your machine.When
someoneelse connects, however, his or her browser can't findit. So when
you'retesting, make sure youerase the relevant .classfiles(or .jar file)
on your local machine to verifythat they exist in theproper location on
theserver.
One of the most insidious placeswhere this happened to me is when I
innocentlyplaced an applet inside a package. After uploading theHTML
fileand applet, it turned outthat the server path to the applet was
confusedbecause of the package name.However, my browser found it in
thelocal CLASSPATH. So I wasthe only one whocould properly loadthe
applet. It took some time to discover that the packagestatementwas the
culprit. In general, you'll want to leave the packagestatementout of an
applet.
Chapter13: Creating Windows & Applets
699
img
Running applets from the
command line
Thereare times when you'dlike to make a windowedprogram do
somethingelse other than sit on a Web page. Perhaps you'dalso like it to
do some of the things a "regular" application can do but still havethe
vauntedinstant portability provided by Java. In previous chapters in this
bookwe've made command-lineapplications, but in someoperating
environments(the Macintosh, for example)there isn't a commandline.
So for any number of reasonsyou'd like to build a windowed, non-applet
programusing Java. This is certainly a reasonabledesire.
TheSwing library allows you to make an application thatpreserves the
lookand feel of the underlyingoperating environment. If youwant to
buildwindowed applications, it makessense to do so5 only if you canuse
thelatest version of Java andassociated tools so you candeliver
applicationsthat won't confound yourusers. If for some reasonyou're
forced to use an older version of Java, think hard beforecommitting to
building a significant windowedapplication.
Oftenyou'll want to be able to create a class that can be invoked as either
a window or an applet. This is especially convenient whenyou're testing
theapplets, since it'stypically much faster andeasier to run theresulting
applet-applicationfrom the command linethan it is to start up a Web
browser or the Appletviewer.
To create an applet that can be run from the consolecommand line, you
simplyadd a main( ) to your applet that builds an instance of theapplet
inside a JFrame.6 As a simpleexample, let's look at Applet1b.java
modified to work as both an applicationand an applet:
//: c13:Applet1c.java
5 In my opinion.And after you learn about Swing, you won't want to wasteyour time on
the earlier stuff.
6 As describedearlier, "Frame" was already taken by the AWT, so Swing uses JFrame.
700
Thinking in Java
img
// An application and an applet.
// < applet code=Applet1c width=100 height=50>
// < /applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Applet1c extends JApplet {
public void init() {
getContentPane().add(newJLabel("Applet!"));
}
// A main() for the application:
public static void main(String[] args) {
JApplet applet = new Applet1c();
JFrame frame = new JFrame("Applet1c");
// To close the application:
Console.setupClosing(frame);
frame.getContentPane().add(applet);
frame.setSize(100,50);
applet.init();
applet.start();
frame.setVisible(true);
}
} ///:~
main( ) is the only element added to the applet, and therest of the applet
is untouched. The applet is created and added to a JFrame so that it can
be displayed.
Theline:
Console.setupClosing(frame);
Causesthe window to be properlyclosed. Consolecomesfrom
com.bruceeckel.swingandwill be explained a littlelater.
Youcan see that in main( ), the applet is explicitlyinitialized andstarted
since in this case the browserisn't available to do it foryou. Of course,
thisdoesn't provide the fullbehavior of the browser,which also calls
Chapter13: Creating Windows & Applets
701
img
stop( ) anddestroy( ), but for most situationsit's acceptable. If it's a
problem,you can force thecalls yourself.7
Noticethe last line:
frame.setVisible(true);
Withoutthis, you won't seeanything on thescreen.
A display framework
Althoughthe code that turnsprograms into both appletsand applications
producesvaluable results, if usedeverywhere it becomes distractingand
wastespaper. Instead, thefollowing display frameworkwill be used for
theSwing examples in the rest of this book:
//: com:bruceeckel:swing:Console.java
// Tool for running Swing demos from the
// console, both applets and JFrames.
package com.bruceeckel.swing;
import javax.swing.*;
import java.awt.event.*;
public class Console {
// Create a title string from the class name:
public static String title(Object o) {
String t = o.getClass().toString();
// Remove the word "class":
if(t.indexOf("class") != -1)
t = t.substring(6);
return t;
}
public static void setupClosing(JFrame frame) {
// The JDK 1.2 Solution as an
// anonymous inner class:
frame.addWindowListener(newWindowAdapter() {
7 This willmake sense after you've readfurther in this chapter. First,make the reference
JAppleta staticmember of the class (instead of a localvariable of main( )), and then
callapplet.stop( ) andapplet.destroy( ) inside
WindowAdapter.windowClosing( ) beforeyou call System.exit( ).
702
Thinking in Java
img
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
// The improved solution in JDK 1.3:
// frame.setDefaultCloseOperation(
//
EXIT_ON_CLOSE);
}
public static void
run(JFrame frame, int width, int height) {
setupClosing(frame);
frame.setSize(width, height);
frame.setVisible(true);
}
public static void
run(JApplet applet, int width, int height) {
JFrame frame = new JFrame(title(applet));
setupClosing(frame);
frame.getContentPane().add(applet);
frame.setSize(width, height);
applet.init();
applet.start();
frame.setVisible(true);
}
public static void
run(JPanel panel, int width, int height) {
JFrame frame = new JFrame(title(panel));
setupClosing(frame);
frame.getContentPane().add(panel);
frame.setSize(width, height);
frame.setVisible(true);
}
} ///:~
This is a tool you may want to use yourself, so it's placed in the library
com.bruceeckel.swing. The Consoleclassconsists entirely of static
methods.The first is used to extractthe class name (usingRTTI) from any
objectand to remove the word"class," which is typicallyprepended by
getClass( ). This uses the StringmethodsindexOf( ) to determine
whetherthe word "class" is there,and substring( ) to produce the new
Chapter13: Creating Windows & Applets
703
img
stringwithout "class" or thetrailing space. This name is used to label the
windowthat is displayed by therun( ) methods.
setupClosing( ) is used to hide the codethat causes a JFrame to exit a
programwhen that JFrame is closed. The default behavior is to do
nothing, so if you don't callsetupClosing( ) or write the equivalentcode
foryour JFrame, the application won't close.The reason this code is
hiddenrather than placing it directly in the subsequentrun( ) methods is
partlybecause it allows you to usethe method by itself whenwhat you
want to do is more complicated thanwhat run( ) provides.However, it
alsoisolates a change factor:Java 2 has two ways of causing certain types
of windows to close. In JDK1.2, the solution is to create a new
WindowAdapter classand implement windowClosing( ), as seen
above(the meaning of this will be fully explained later in this chapter).
However,during the creation of JDK1.3 the library designersobserved
thatyou typically need to closewindows whenever you'recreating a non-
applet,and so they added thesetDefaultCloseOperation( ) to
JFrame andJDialog. From the standpoint of writing code, thenew
method is much nicer to use butthis book was writtenwhile there was
still no JDK 1.3 implemented on Linux and other platforms, so in the
interest of cross-version portability thechange was isolatedinside
setupClosing( ).
Therun( ) method is overloaded to work withJApplets, JPanels, and
JFrames. Note that only if it's a JApplet areinit( ) andstart( ) called.
Nowany applet can be runfrom the console by creating a main( )
containing a line like this:
Console.run(new MyClass(), 500, 300);
in which the last twoarguments are the displaywidth and height.Here's
Applet1c.javamodified to use Console:
//: c13:Applet1d.java
// Console runs applets from the command line.
// < applet code=Applet1d width=100 height=50>
// < /applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
704
Thinking in Java
img
public class Applet1d extends JApplet {
public void init() {
getContentPane().add(newJLabel("Applet!"));
}
public static void main(String[] args) {
Console.run(newApplet1d(), 100, 50);
}
} ///:~
Thisallows the elimination of repeated code whileproviding thegreatest
flexibility in running theexamples.
Using the Windows Explorer
If you're using Windows, youcan simplify the process of running a
command-lineJava program by configuringthe WindowsExplorer--the
filebrowser in Windows, not theInternet Explorer--so thatyou can
simplydouble-click on a .classfile to execute it. There areseveral steps in
thisprocess.
First,download and install thePerl programming languagefrom
www.Perl.org. You'll find the instructionsand languagedocumentation
on that site.
Next,create the following scriptwithout the first andlast lines (thisscript
is part of this book'ssource-code package):
//:! c13:RunJava.bat
@rem = '--*-Perl-*--
@echo off
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';
#!perl
$file = $ARGV[0];
$file =~ s/(.*)\..*/\1/;
$file =~ s/(.*\\)*(.*)/$+/;
`java $file`;
__END__
:endofperl
///:~
Chapter13: Creating Windows & Applets
705
img
Now,open the Windows Explorer,select "View," "FolderOptions," then
click on the "File Types" tab.Press the "New Type"button. For
"Description of Type" enter "Java classfile." For "AssociatedExtension,"
enter"class." Under "Actions"press the "New" button.For "Action" enter
"Open,"and for "Application used to perform action" enter a linelike this:
"c:\aaa\Perl\RunJava.bat" "%L"
Youmust customize the pathbefore "RunJava.bat" to conform to the
locationwhere you placed thebatch file.
Onceyou perform thisinstallation, you may runany Java program by
simplydouble-clicking on the .classfilecontaining a main( ).
Making a button
Making a button is quite simple:you just call theJButton constructor
withthe label you want on the button. You'll seelater that you can do
fancierthings, like putting graphicimages on buttons.
Usuallyyou'll want to create a field for the buttoninside your class so that
youcan refer to it later.
TheJButton is a component--its own littlewindow--that will
automaticallyget repainted as part of an update. This means thatyou
don'texplicitly paint a button or any other kind of control;you simply
placethem on the form andlet them automatically takecare of painting
themselves. So to place a button on a form,you do it inside init( ):
//: c13:Button1.java
// Putting buttons on an applet.
// < applet code=Button1 width=200 height=50>
// < /applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Button1 extends JApplet {
JButton
b1 = new JButton("Button 1"),
b2 = new JButton("Button 2");
706
Thinking in Java
img
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(b1);
cp.add(b2);
}
public static void main(String[] args) {
Console.run(new Button1(), 200, 50);
}
} ///:~
Somethingnew has been addedhere: before any elementsare placed on
thecontent pane, it is given a new "layout manager," of type
FlowLayout. The layout manager is theway that the paneimplicitly
decideswhere to place the control on the form. The normalbehavior of an
applet is to use the BorderLayout, but that won't workhere because (as
youwill learn later in thischapter when controlling thelayout of a form is
examined in more detail) it defaults to covering each controlentirely with
everynew one that is added.However, FlowLayout causesthe controls
to flow evenly onto theform, left to right andtop to bottom.
Capturing an event
You'llnotice that if you compileand run the appletabove, nothing
happenswhen you press thebuttons. This is where youmust step in and
writesome code to determine whatwill happen. The basis of event-driven
programming,which comprises a lot of what a GUI is about, is tying
events to code that responds to those events.
Theway that this is accomplished in Swing is by cleanlyseparating the
interface(the graphical components)and the implementation (thecode
thatyou want to run when an event happens to a component).Each Swing
componentcan report all theevents that might happen to it, and it can
reporteach kind of eventindividually. So if you're notinterested in, for
example,whether the mouse is beingmoved over your button,you don't
registeryour interest in that event.It's a very straightforwardand elegant
way to handle event-driven programming,and once you understandthe
basicconcepts you can easilyuse Swing components thatyou haven't seen
Chapter13: Creating Windows & Applets
707
img
before--infact, this model extends to anything that can be classified as a
JavaBean(which you'll learn aboutlater in thechapter).
At first, we will just focus on the main event of interestfor the
componentsbeing used. In the case of a JButton, this "event of interest"
is that the button is pressed. To register your interest in when a button is
pressed,you call the JButton's addActionListener( ) method.This
methodexpects an argument that is an object that implementsthe
ActionListenerinterface,which contains a singlemethod called
actionPerformed( ). So all you have to do to attach code to a JButton
is to implement the ActionListenerinterface in a class, and register an
object of that class with theJButton viaaddActionListener( ). The
methodwill be called when thebutton is pressed (this is normally referred
to as a callback).
Butwhat should the result of pressing that button be?We'd like to see
somethingchange on the screen, so a new Swing component will be
introduced:the JTextField. This is a place where textcan be typed, or in
thiscase modified by theprogram. Although there are a number of ways
to create a JTextField, the simplest is just to tellthe constructor how
wideyou want that field to be. Once the JTextFieldis placed on the
form,you can modify itscontents by using thesetText( ) method(there
aremany other methods in JTextField, but you must lookthese up in
theHTML documentation for theJDK from java.sun.com). Here is what
it looks like:
//: c13:Button2.java
// Responding to button presses.
// < applet code=Button2 width=200 height=75>
// < /applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class
Button2 extends JApplet {
JButton
b1 = new
JButton("Button 1"),
b2 = new
JButton("Button 2");
JTextField
txt = new JTextField(10);
708
Thinking in Java
img
class BL implements ActionListener {
public void actionPerformed(ActionEvent e){
String name =
((JButton)e.getSource()).getText();
txt.setText(name);
}
}
BL al = new BL();
public void init() {
b1.addActionListener(al);
b2.addActionListener(al);
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(b1);
cp.add(b2);
cp.add(txt);
}
public static void main(String[] args) {
Console.run(new Button2(), 200, 75);
}
} ///:~
Creating a JTextFieldandplacing it on the canvastakes the samesteps
as for JButtons, or for any Swing component.The difference in theabove
program is in the creation of theaforementioned ActionListenerclass
BL. The argument to actionPerformed( ) is of type ActionEvent,
whichcontains all the informationabout the event andwhere it came
from. In this case, I wanted to describe the button thatwas pressed:
getSource( ) producesthe object where theevent originated, and I
assumedthat is a JButton. getText( ) returnsthe text that's on the
button,and this is placed in theJTextFieldto prove that the codewas
actuallycalled when the buttonwas pressed.
In init( ), addActionListener( ) is used to register theBL objectwith
boththe buttons.
It is often more convenient to code the ActionListeneras an
anonymousinner class, especiallysince you tend to onlyuse a single
instance of each listener class.Button2.javacan be modified to use an
anonymousinner class as follows:
Chapter13: Creating Windows & Applets
709
img
//: c13:Button2b.java
// Using anonymous inner classes.
// < applet code=Button2b width=200 height=75>
// < /applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Button2b extends JApplet {
JButton
b1 = new JButton("Button 1"),
b2 = new JButton("Button 2");
JTextField txt = new JTextField(10);
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e){
String name =
((JButton)e.getSource()).getText();
txt.setText(name);
}
};
public void init() {
b1.addActionListener(al);
b2.addActionListener(al);
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(b1);
cp.add(b2);
cp.add(txt);
}
public static void main(String[] args) {
Console.run(newButton2b(), 200, 75);
}
} ///:~
Theapproach of using an anonymousinner class will be preferred (when
possible)for the examples in thisbook.
710
Thinking in Java
img
Text areas
A JTextArea is like a JTextFieldexceptthat it can have multiplelines
andhas more functionality. A particularly useful method is append( );
withthis you can easilypour output into theJTextArea, thus making a
Swingprogram an improvement (sinceyou can scroll backward)over
whathas been accomplished thusfar using command-lineprograms that
print to standard output. As an example,the following program fills a
JTextArea withthe output from thegeographygenerator in Chapter 9:
//: c13:TextArea.java
// Using the JTextArea control.
// < applet code=TextArea width=475 height=425>
// < /applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import com.bruceeckel.swing.*;
import com.bruceeckel.util.*;
public class TextArea extends JApplet {
JButton
b = new JButton("Add Data"),
c = new JButton("Clear Data");
JTextArea t = new JTextArea(20, 40);
Map m = new HashMap();
public void init() {
// Use up all the data:
Collections2.fill(m,
Collections2.geography,
CountryCapitals.pairs.length);
b.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
for(Iterator it= m.entrySet().iterator();
it.hasNext();){
Map.Entry me = (Map.Entry)(it.next());
t.append(me.getKey() + ": "
+ me.getValue() + "\n");
}
Chapter13: Creating Windows & Applets
711
img
}
});
c.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
t.setText("");
}
});
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(new JScrollPane(t));
cp.add(b);
cp.add(c);
}
public static void main(String[] args) {
Console.run(newTextArea(), 475, 425);
}
} ///:~
In init( ), the Map is filled with all thecountries and theircapitals. Note
thatfor both buttons theActionListeneris created and addedwithout
defining an intermediate variable, sinceyou never need to refer to that
listeneragain during the program.The "Add Data" buttonformats and
appendsall the data, whilethe "Clear Data" buttonuses setText( ) to
removeall the text fromthe JTextArea.
As the JTextArea is added to the applet, it is wrapped in a
JScrollPane, to control scrolling whentoo much text is placed on the
screen.That's all you must do in order to produce fullscrolling
capabilities.Having tried to figure outhow to do the equivalent in some
other GUI programming environments, I am veryimpressed with the
simplicityand good design of components like JScrollPane.
Controlling layout
Theway that you placecomponents on a form in Java is probably
differentfrom any other GUI systemyou've used. First, it'sall code; there
are no "resources" that controlplacement of components. Second,the way
componentsare placed on a form is controlled not by absolutepositioning
but by a "layout manager" thatdecides how the componentslie based on
theorder that you add( ) them.The size, shape, andplacement of
712
Thinking in Java
img
componentswill be remarkably differentfrom one layout manager to
another. In addition, the layoutmanagers adapt to thedimensions of your
applet or application window, so if thewindow dimension is changed,the
size,shape, and placement of thecomponents can change in response.
JApplet, JFrame JWindow, andJDialogcanall produce a
ContainerwithgetContentPane( ) thatcan contain anddisplay
Components. In Container,there's a method called setLayout( ) that
allowsyou to choose a differentlayout manager. Otherclasses, such as
JPanel, contain and displaycomponents directly and so you also setthe
layoutmanager directly, withoutusing the contentpane.
In this section we'll explorethe various layout managers by placing
buttons in them (since that's thesimplest thing to do). Therewon't be any
capturing of button events since theseexamples are just intended to show
howthe buttons are laidout.
BorderLayout
Theapplet uses a default layoutscheme: the BorderLayout(a number
of the previous example havechanged the layout manager to
FlowLayout). Without any otherinstruction, this takeswhatever you
add( ) to it and places it in thecenter, stretching theobject all theway
out to the edges.
However,there's more to theBorderLayout. This layout managerhas
theconcept of four borderregions and a center area.When you add
something to a panel that's using a BorderLayoutyoucan use the
overloadedadd( ) methodthat takes a constant value as its first
argument.This value can be any of the following:
BorderLayout. NORTH (top)
BorderLayout. SOUTH (bottom)
BorderLayout. EAST (right)
BorderLayout. WEST (left)
BorderLayout.CENTER(fillthe middle, up to theother
components or to the edges)
If you don't specify an area to place the object, it defaults to CENTER.
Chapter13: Creating Windows & Applets
713
img
Here's a simple example. Thedefault layout is used,since JApplet
defaults to BorderLayout:
//: c13:BorderLayout1.java
// Demonstrates BorderLayout.
// < applet code=BorderLayout1
// width=300 height=250> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class BorderLayout1 extends JApplet {
public void init() {
Container cp = getContentPane();
cp.add(BorderLayout.NORTH,
new JButton("North"));
cp.add(BorderLayout.SOUTH,
new JButton("South"));
cp.add(BorderLayout.EAST,
new JButton("East"));
cp.add(BorderLayout.WEST,
new JButton("West"));
cp.add(BorderLayout.CENTER,
new JButton("Center"));
}
public static void main(String[] args) {
Console.run(newBorderLayout1(), 300, 250);
}
} ///:~
Forevery placement butCENTER, the element that youadd is
compressed to fit in the smallestamount of space along onedimension
while it is stretched to the maximumalong the otherdimension.
CENTER, however, spreads out in bothdimensions to occupythe
middle.
FlowLayout
Thissimply "flows" thecomponents onto the form,from left to rightuntil
thetop space is full, thenmoves down a row andcontinues flowing.
714
Thinking in Java
img
Here's an example that sets thelayout manager to FlowLayout andthen
placesbuttons on the form. You'llnotice that with FlowLayout the
componentstake on their "natural"size. A JButton, for example, will be
thesize of its string.
//: c13:FlowLayout1.java
// Demonstrates FlowLayout.
// < applet code=FlowLayout1
// width=300 height=250> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class FlowLayout1 extends JApplet {
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
for(int i = 0; i < 20; i++)
cp.add(new JButton("Button " + i));
}
public static void main(String[] args) {
Console.run(newFlowLayout1(), 300, 250);
}
} ///:~
Allcomponents will be compacted to their smallest size in a
FlowLayout, so you might get a littlebit of surprising behavior.For
example,because a JLabelwill be the size of its string,attempting to
right-justifyits text yields an unchangeddisplay when using
FlowLayout.
GridLayout
A GridLayout allowsyou to build a table of components, and as youadd
themthey are placedleft-to-right and top-to-bottom in the grid. In the
constructoryou specify the number of rows and columns thatyou need
andthese are laid out in equal proportions.
//: c13:GridLayout1.java
// Demonstrates GridLayout.
// < applet code=GridLayout1
Chapter13: Creating Windows & Applets
715
img
// width=300 height=250> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class GridLayout1 extends JApplet {
public void init() {
Container cp = getContentPane();
cp.setLayout(newGridLayout(7,3));
for(int i = 0; i < 20; i++)
cp.add(new JButton("Button " + i));
}
public static void main(String[] args) {
Console.run(newGridLayout1(), 300, 250);
}
} ///:~
In this case there are 21 slots but only 20 buttons.The last slot is left
emptybecause no "balancing" goes on with a GridLayout.
GridBagLayout
TheGridBagLayoutprovidesyou with tremendous control in deciding
exactlyhow the regions of yourwindow will lay themselvesout and
reformatthemselves when the window is resized. However, it'salso the
mostcomplicated layout manager,and quite difficult to understand. It is
intendedprimarily for automatic codegeneration by a GUI builder(good
GUI builders will use GridBagLayoutinstead of absolute placement). If
yourdesign is so complicated thatyou feel you need to use
GridBagLayout, then you should be using a GUI builder tool to
generatethat design. If you feelyou must know theintricate details,I'll
referyou to CoreJava 2 by Horstmann & Cornell (Prentice-Hall,1999),
or a dedicated Swing book, as a starting point.
Absolutepositioning
It is also possible to set theabsolute position of thegraphical components
in this way:
1.
Set a null layoutmanager for your Container: setLayout(null).
716
Thinking in Java
img
2.
CallsetBounds( ) or reshape( ) (depending on the language
version)for each component, passing a bounding rectangle in pixel
coordinates.You can do this in theconstructor, or in paint( ),
depending on what you want to achieve.
Some GUI builders use thisapproach extensively, butthis is usually not
thebest way to generate code.More useful GUI builderswill use
GridBagLayoutinstead.
BoxLayout
Becausepeople had so much troubleunderstanding and workingwith
GridBagLayout, Swing also includes theBoxLayout, which gives you
many of the benefits of GridBagLayoutwithoutthe complexity, so you
canoften use it when youneed to do hand-coded layouts(again, if your
designbecomes too complex, use a GUI builder thatgenerates
GridBagLayouts for you). BoxLayoutallowsyou to control the
placement of components either vertically or horizontally, and to control
thespace between the componentsusing something called"struts and
glue."First, let's see how to use the BoxLayoutdirectly, in the same way
thatthe other layout managershave beendemonstrated:
//: c13:BoxLayout1.java
// Vertical and horizontal BoxLayouts.
// < applet code=BoxLayout1
// width=450 height=200> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class BoxLayout1 extends JApplet {
public void init() {
JPanel jpv = new JPanel();
jpv.setLayout(
new BoxLayout(jpv, BoxLayout.Y_AXIS));
for(int i = 0; i < 5; i++)
jpv.add(new JButton("" + i));
JPanel jph = new JPanel();
jph.setLayout(
new BoxLayout(jph, BoxLayout.X_AXIS));
for(int i = 0; i < 5; i++)
Chapter13: Creating Windows & Applets
717
img
jph.add(new JButton("" + i));
Container cp = getContentPane();
cp.add(BorderLayout.EAST, jpv);
cp.add(BorderLayout.SOUTH, jph);
}
public static void main(String[] args) {
Console.run(newBoxLayout1(), 450, 200);
}
} ///:~
Theconstructor for BoxLayoutis a bit different than theother layout
managers--youprovide the Containerthat is to be controlled by the
BoxLayoutas the first argument, andthe direction of the layout as the
secondargument.
To simplify matters, there's a special container calledBox thatuses
BoxLayoutas its native manager. Thefollowing example laysout
componentshorizontally and verticallyusing Box, which has two static
methods to create boxes withvertical and horizontalalignment:
//: c13:Box1.java
// Vertical and horizontal BoxLayouts.
// < applet code=Box1
// width=450 height=200> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box1 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
for(int i = 0; i < 5; i++)
bv.add(new JButton("" + i));
Box bh = Box.createHorizontalBox();
for(int i = 0; i < 5; i++)
bh.add(new JButton("" + i));
Container cp = getContentPane();
cp.add(BorderLayout.EAST, bv);
cp.add(BorderLayout.SOUTH, bh);
}
public static void main(String[] args) {
718
Thinking in Java
img
Console.run(new Box1(), 450, 200);
}
} ///:~
Onceyou have a Box, you pass it as a secondargument when adding
components to the content pane.
Strutsadd space betweencomponents, measured in pixels. To use a strut,
yousimply add it in between theaddition of the componentsthat you
wantspaced apart:
//: c13:Box2.java
// Adding struts.
// < applet code=Box2
// width=450 height=300> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box2 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
for(int i = 0; i < 5; i++) {
bv.add(new JButton("" + i));
bv.add(Box.createVerticalStrut(i*10));
}
Box bh = Box.createHorizontalBox();
for(int i = 0; i < 5; i++) {
bh.add(new JButton("" + i));
bh.add(Box.createHorizontalStrut(i*10));
}
Container cp = getContentPane();
cp.add(BorderLayout.EAST, bv);
cp.add(BorderLayout.SOUTH, bh);
}
public static void main(String[] args) {
Console.run(new Box2(), 450, 300);
}
} ///:~
Strutsseparate components by a fixedamount, but glue is theopposite: it
separatescomponents by as much as possible.Thus it's more of a "spring"
Chapter13: Creating Windows & Applets
719
img
than"glue" (and the design on which this was basedwas called "springs
andstruts" so the choice of theterm is a bitmysterious).
//: c13:Box3.java
// Using Glue.
// < applet code=Box3
// width=450 height=300> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box3 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
bv.add(new JLabel("Hello"));
bv.add(Box.createVerticalGlue());
bv.add(new JLabel("Applet"));
bv.add(Box.createVerticalGlue());
bv.add(new JLabel("World"));
Box bh = Box.createHorizontalBox();
bh.add(new JLabel("Hello"));
bh.add(Box.createHorizontalGlue());
bh.add(new JLabel("Applet"));
bh.add(Box.createHorizontalGlue());
bh.add(new JLabel("World"));
bv.add(Box.createVerticalGlue());
bv.add(bh);
bv.add(Box.createVerticalGlue());
getContentPane().add(bv);
}
public static void main(String[] args) {
Console.run(new Box3(), 450, 300);
}
} ///:~
A strut works in onedirection, but a rigid areafixes the spacingbetween
components in both directions:
//: c13:Box4.java
// Rigid Areas are like pairs of struts.
// < applet code=Box4
// width=450 height=300> </applet>
720
Thinking in Java
img
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box4 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
bv.add(new JButton("Top"));
bv.add(Box.createRigidArea(
new Dimension(120, 90)));
bv.add(new JButton("Bottom"));
Box bh = Box.createHorizontalBox();
bh.add(new JButton("Left"));
bh.add(Box.createRigidArea(
new Dimension(160, 80)));
bh.add(new JButton("Right"));
bv.add(bh);
getContentPane().add(bv);
}
public static void main(String[] args) {
Console.run(new Box4(), 450, 300);
}
} ///:~
Youshould be aware that rigidareas are a bitcontroversial. Since theyuse
absolutevalues, some people feelthat they cause moretrouble than they
areworth.
The best approach?
Swing is powerful; it can get a lot done with a fewlines of code. The
examplesshown in this book arereasonably simple, and forlearning
purposes it makes sense to write them by hand. You canactually
accomplishquite a bit by combiningsimple layouts. At somepoint,
however, it stops making sense to hand-code GUI forms--it becomestoo
complicatedand is not a good use of your programming time. TheJava
andSwing designers oriented thelanguage and libraries to support GUI
buildingtools, which have beencreated for the expresspurpose of making
yourprogramming experience easier. As long as you understandwhat's
going on with layouts and how to deal with the events(described next),
it'snot particularly importantthat you actually knowthe details of how to
Chapter13: Creating Windows & Applets
721
img
layout components by hand--letthe appropriate tool do thatfor you
(Javais, after all, designed to increase programmerproductivity).
The Swing event model
In the Swing event model a component can initiate("fire") an event.Each
type of event is represented by a distinctclass. When an event is fired, it is
received by one or more "listeners,"which act on that event.Thus, the
source of an event and the placewhere the event is handledcan be
separate.Since you typically useSwing components as theyare, but need
to write code that is calledwhen the components receive an event, this is
an excellent example of theseparation of interface andimplementation.
Eachevent listener is an object of a class that implements a particular
type of listener interface. So as a programmer, all you do is create a
listenerobject and register it withthe component that's firingthe event.
Thisregistration is performed by calling an addXXXListener( ) method
in the event-firing component, in which "XXX" represents the type of
eventlistened for. You caneasily know what types of events can be
handled by noticing the names of the"addListener" methods, and if you
try to listen for the wrongevents you'll discover yourmistake at compile-
time.You'll see later in thechapter that JavaBeans alsouse the names of
the"addListener" methods to determinewhat events a Bean canhandle.
All of your event logic, then,will go inside a listenerclass. When you
create a listener class, the solerestriction is that it mustimplement the
appropriateinterface. You can create a global listener class, butthis is a
situation in which inner classes tend to be quite useful, not onlybecause
theyprovide a logical grouping of your listener classes insidethe UI or
businesslogic classes they areserving, but because (asyou shall seelater)
thefact that an inner classobject keeps a reference to its parent object
provides a nice way to call acrossclass and subsystemboundaries.
Allthe examples so far in thischapter have been usingthe Swing event
model,but the remainder of thissection will fill outthe details of that
model.
722
Thinking in Java
img
Event and listener types
AllSwing components includeaddXXXListener( ) and
removeXXXListener( ) methods so that the appropriatetypes of
listenerscan be added and removedfrom each component. You'llnotice
thatthe "XXX" in each case also representsthe argument for themethod,
forexample: addMyListener(MyListener m). The following table
includesthe basic associated events,listeners, and methods,along with
thebasic components thatsupport those particularevents by providing
theaddXXXListener( ) andremoveXXXListener( ) methods.You
shouldkeep in mind that theevent model is designed to be extensible, so
youmay encounter other eventsand listener types thatare not covered in
thistable.
Event,listener interface and
Componentssupporting this
add- and remove-methods
event
ActionEvent
JButton, JList, JTextField,
ActionListener
JMenuItem andits derivatives
addActionListener( )
includingJCheckBoxMenuItem,
removeActionListener( )
JMenu, andJpopupMenu.
AdjustmentEvent
JScrollbar
AdjustmentListener
andanything you createthat
addAdjustmentListener( )
implementsthe Adjustable
removeAdjustmentListener( )
interface.
ComponentEvent
*Componentandits derivatives,
ComponentListener
includingJButton, JCanvas,
addComponentListener( )
JCheckBox,JComboBox,
removeComponentListener( )
Container,JPanel, JApplet,
JScrollPane, Window, JDialog,
JFileDialog,JFrame, JLabel,
JList,JScrollbar, JTextArea, and
JTextField.
ContainerEvent
Containerandits derivatives,
ContainerListener
includingJPanel,JApplet,
addContainerListener( )
JScrollPane, Window, JDialog,
removeContainerListener( )
JFileDialog,andJFrame.
FocusEvent
Componentandderivatives*.
FocusListener
addFocusListener( )
removeFocusListener( )
KeyEvent
Componentandderivatives*.
Chapter13: Creating Windows & Applets
723
img
Event,listener interface and
Componentssupporting this
add- and remove-methods
event
KeyListener
addKeyListener( )
removeKeyListener( )
MouseEvent (forboth clicks and
Componentandderivatives*.
motion)
MouseListener
addMouseListener( )
removeMouseListener( )
MouseEvent8 (forboth clicks and
Componentandderivatives*.
motion)
MouseMotionListener
addMouseMotionListener( )
removeMouseMotionListener( )
WindowEvent
Window andits derivatives,
WindowListener
includingJDialog,JFileDialog,
addWindowListener( )
and JFrame.
removeWindowListener( )
ItemEvent
JCheckBox,
ItemListener
JCheckBoxMenuItem,
addItemListener( )
JComboBox,JList, andanything
removeItemListener( )
thatimplements the
ItemSelectableinterface.
TextEvent
Anythingderived from
TextListener
JTextComponent,including
addTextListener( )
JTextAreaandJTextField.
removeTextListener( )
Youcan see that eachtype of component supportsonly certain types of
events. It turns out to be ratherdifficult to look up all theevents
supported by each component. A simplerapproach is to modifythe
ShowMethodsClean.javaprogramfrom Chapter 12 so that it displays
allthe event listenerssupported by any Swingcomponent that youenter.
Chapter 12 introduced reflectionandused that feature to look up methods
for a particular class--either theentire list of methods or a subset of those
8 There is no MouseMotionEventeven though it seems like there ought to be. Clicking
andmotion is combined intoMouseEvent, so this second appearance of MouseEvent
in the table is not an error.
724
Thinking in Java
img
whosenames match a keyword thatyou provide. The magic of this is that
it can automatically show youall themethods for a class withoutforcing
you to walk up the inheritancehierarchy examining the baseclasses at
eachlevel. Thus, it provides a valuable timesaving tool forprogramming:
becausethe names of most Javamethods are made nicelyverbose and
descriptive,you can search forthe method names thatcontain a particular
word of interest. When you findwhat you think you'relooking for, check
theonline documentation.
However, by Chapter 12 you hadn'tseen Swing, so the tool in that chapter
wasdeveloped as a command-line application.Here is the moreuseful
GUI version, specialized to lookfor the "addListener"methods in Swing
components:
//: c13:ShowAddListeners.java
// Display the "addXXXListener" methods of any
// Swing class.
// < applet code = ShowAddListeners
// width=500 height=400></applet>
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import java.io.*;
import com.bruceeckel.swing.*;
import com.bruceeckel.util.*;
public class ShowAddListeners extends JApplet {
Class cl;
Method[] m;
Constructor[] ctor;
String[] n = new String[0];
JTextField name = new JTextField(25);
JTextArea results = new JTextArea(40, 65);
class NameL implements ActionListener {
public void actionPerformed(ActionEvent e) {
String nm = name.getText().trim();
if(nm.length() == 0) {
results.setText("No match");
n = new String[0];
Chapter13: Creating Windows & Applets
725
img
return;
}
try {
cl = Class.forName("javax.swing." + nm);
} catch(ClassNotFoundException ex) {
results.setText("No match");
return;
}
m = cl.getMethods();
// Convert to an array of Strings:
n = new String[m.length];
for(int i = 0; i < m.length; i++)
n[i] = m[i].toString();
reDisplay();
}
}
void reDisplay() {
// Create the result set:
String[] rs = new String[n.length];
int j = 0;
for (int i = 0; i < n.length; i++)
if(n[i].indexOf("add") != -1 &&
n[i].indexOf("Listener") != -1)
rs[j++] =
n[i].substring(n[i].indexOf("add"));
results.setText("");
for (int i = 0; i < j; i++)
results.append(
StripQualifiers.strip(rs[i]) + "\n");
}
public void init() {
name.addActionListener(new NameL());
JPanel top = new JPanel();
top.add(new JLabel(
"Swing class name (press ENTER):"));
top.add(name);
Container cp = getContentPane();
cp.add(BorderLayout.NORTH, top);
cp.add(new JScrollPane(results));
}
public static void main(String[] args) {
726
Thinking in Java
img
Console.run(newShowAddListeners(), 500,400);
}
} ///:~
TheStripQualifiersclassdefined in Chapter 12 is reusedhere by
importingthe com.bruceeckel.utillibrary.
The GUI contains a JTextField name in which you can enterthe Swing
classname you want to lookup. The results aredisplayed in a
JTextArea.
You'llnotice that there are no buttons or other components by which to
indicatethat you want thesearch to begin. That'sbecause the JTextField
is monitored by an ActionListener. Whenever you make a changeand
pressENTER, the list is immediately updated. If thetext isn't empty, it is
usedinside Class.forName( ) to try to look up the class. If the name is
incorrect,Class.forName( ) willfail, which means that it throws an
exception.This is trapped and theJTextArea is set to "No match." But if
youtype in a correct name(capitalization counts), Class.forName( ) is
successfuland getMethods( ) willreturn an array of Method objects.
Each of the objects in the array is turned into a StringviatoString( )
(thisproduces the complete methodsignature) and added to n, a String
array.The array n is a member of classShowAddListeners and is used
in updating the displaywhenever reDisplay( ) is called.
reDisplay( ) creates an array of Stringcalledrs (for"result set"). The
resultset is conditionally copiedfrom the Strings in n thatcontain "add"
and"Listener." indexOf( ) andsubstring( ) arethen used to remove
thequalifiers like public, static, etc. Finally, StripQualifiers.strip( )
removesthe extra namequalifiers.
Thisprogram is a convenient way to investigate the capabilities of a Swing
component.Once you know whichevents a particularcomponent
supports,you don't need to lookanything up to react to thatevent. You
simply:
1.
Takethe name of the eventclass and remove theword "Event."
Addthe word "Listener" to what remains. This is thelistener
interfaceyou must implement in yourinner class.
Chapter13: Creating Windows & Applets
727
img
2.
Implementthe interface above andwrite out the methodsfor the
eventsyou want to capture. Forexample, you might be looking for
mousemovements, so you write codefor the mouseMoved( )
method of the MouseMotionListenerinterface.(You must
implementthe other methods, of course, but there's often a
shortcutfor that which you'llsee soon.)
3.
Create an object of the listenerclass in Step 2. Register it with your
componentwith the method produced by prefixing "add" to your
listenername. For example, addMouseMotionListener( ).
Hereare some of the listenerinterfaces:
Listenerinterface
Methods in interface
w/ adapter
ActionListener
actionPerformed(ActionEvent)
AdjustmentListener
adjustmentValueChanged(
AdjustmentEvent)
ComponentListener
componentHidden(ComponentEvent)
ComponentAdapter
componentShown(ComponentEvent)
componentMoved(ComponentEvent)
componentResized(ComponentEvent)
ContainerListener
componentAdded(ContainerEvent)
ContainerAdapter
componentRemoved(ContainerEvent)
FocusListener
focusGained(FocusEvent)
FocusAdapter
focusLost(FocusEvent)
KeyListener
keyPressed(KeyEvent)
KeyAdapter
keyReleased(KeyEvent)
keyTyped(KeyEvent)
MouseListener
mouseClicked(MouseEvent)
MouseAdapter
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)
MouseMotionListener
mouseDragged(MouseEvent)
MouseMotionAdapter
mouseMoved(MouseEvent)
WindowListener
windowOpened(WindowEvent)
WindowAdapter
windowClosing(WindowEvent)
728
Thinking in Java
img
Listenerinterface
Methods in interface
w/ adapter
windowClosed(WindowEvent)
windowActivated(WindowEvent)
windowDeactivated(WindowEvent)
windowIconified(WindowEvent)
windowDeiconified(WindowEvent)
ItemListener
itemStateChanged(ItemEvent)
This is not an exhaustive listing,partly because the eventmodel allows
you to create your own eventtypes and associatedlisteners. Thus,you'll
regularlycome across libraries thathave invented their ownevents, and
theknowledge gained in thischapter will allow you to figure out how to
usethese events.
Usinglistener adapters forsimplicity
In the table above, youcan see that somelistener interfaces haveonly one
method.These are trivial to implement since you'llimplement them only
whenyou want to write thatparticular method. However,the listener
interfacesthat have multiple methodscan be less pleasant to use.For
example,something you must always do when creating an application is
provide a WindowListener to the JFrame so that when you getthe
windowClosing( ) eventyou can call System.exit( ) to exit the
application.But since WindowListener is an interface, you must
implementall of the other methodseven if they don't do anything. This
can be annoying.
To solve the problem, some(but not all) of thelistener interfacesthat
havemore than one methodare provided with adapters, the names of
whichyou can see in thetable above. Each adapterprovides defaultempty
methodsfor each of the interfacemethods. Then all youneed to do is
inheritfrom the adapter andoverride only the methodsyou need to
change.For example, the typicalWindowListener you'lluse looks like
this(remember that this hasbeen wrapped inside theConsoleclass in
com.bruceeckel.swing):
class MyWindowListener extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
Chapter13: Creating Windows & Applets
729
img
}
}
Thewhole point of the adapters is to make the creation of listener classes
easy.
There is a downside to adapters, however, in the form of a pitfall.Suppose
youwrite a WindowAdapter likethe one above:
class MyWindowListener extends WindowAdapter {
public void WindowClosing(WindowEvent e) {
System.exit(0);
}
}
Thisdoesn't work, but it willdrive you crazy trying to figure out why,
sinceeverything will compile andrun fine--except thatclosing the
windowwon't exit the program.Can you see theproblem? It's in thename
of the method: WindowClosing( ) instead of windowClosing( ). A
simpleslip in capitalization results in the addition of a completelynew
method.However, this is not themethod that's called whenthe window is
closing, so you don't get thedesired results. Despite theinconvenience, an
interfacewillguarantee that the methodsare properlyimplemented.
Trackingmultiple events
To prove to yourself that theseevents are in fact beingfired, and as an
interestingexperiment, it's worthcreating an applet thattracks extra
behavior in a JButton (otherthan just whether it'spressed or not). This
examplealso shows you how to inherit your own buttonobject because
that'swhat is used as the target of all the events of interest. To do so,you
canjust inherit from JButton.9
TheMyButton class is an inner class of TrackEvent, so MyButton can
reachinto the parent windowand manipulate its textfields, which is
what'snecessary to be able to writethe status information intothe fields
of the parent. Of course this is a limited solution, sincemyButton can be
9 In Java1.0/1.1 you couldnotusefully inherit from the button object.This was only one of
numerous fundamental design flaws.
730
Thinking in Java
img
usedonly in conjunction withTrackEvent. This kind of code is
sometimescalled "highlycoupled":
//: c13:TrackEvent.java
// Show events as they happen.
// < applet code=TrackEvent
//   width=700 height=500></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.bruceeckel.swing.*;
public class TrackEvent extends JApplet {
HashMap h = new HashMap();
String[] event = {
"focusGained","focusLost", "keyPressed",
"keyReleased","keyTyped", "mouseClicked",
"mouseEntered","mouseExited","mousePressed",
"mouseReleased","mouseDragged", "mouseMoved"
};
MyButton
b1 = new MyButton(Color.blue, "test1"),
b2 = new MyButton(Color.red, "test2");
class MyButton extends JButton {
void report(String field, String msg) {
((JTextField)h.get(field)).setText(msg);
}
FocusListener fl = new FocusListener() {
public void focusGained(FocusEvent e) {
report("focusGained",e.paramString());
}
public void focusLost(FocusEvent e) {
report("focusLost",e.paramString());
}
};
KeyListener kl = new KeyListener() {
public void keyPressed(KeyEvent e) {
report("keyPressed",e.paramString());
}
public void keyReleased(KeyEvent e) {
Chapter13: Creating Windows & Applets
731
img
report("keyReleased",e.paramString());
}
public void keyTyped(KeyEvent e) {
report("keyTyped",e.paramString());
}
};
MouseListener ml = new MouseListener() {
public void mouseClicked(MouseEvent e) {
report("mouseClicked",e.paramString());
}
public void mouseEntered(MouseEvent e) {
report("mouseEntered",e.paramString());
}
public void mouseExited(MouseEvent e) {
report("mouseExited",e.paramString());
}
public void mousePressed(MouseEvent e) {
report("mousePressed",e.paramString());
}
public void mouseReleased(MouseEvent e) {
report("mouseReleased",e.paramString());
}
};
MouseMotionListener mml =
new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
report("mouseDragged",e.paramString());
}
public void mouseMoved(MouseEvent e) {
report("mouseMoved",e.paramString());
}
};
public MyButton(Color color, String label) {
super(label);
setBackground(color);
addFocusListener(fl);
addKeyListener(kl);
addMouseListener(ml);
addMouseMotionListener(mml);
}
}
732
Thinking in Java
img
public void init() {
Container c = getContentPane();
c.setLayout(newGridLayout(event.length+1,2));
for(int i = 0; i < event.length; i++) {
JTextField t = new JTextField();
t.setEditable(false);
c.add(new JLabel(event[i], JLabel.RIGHT));
c.add(t);
h.put(event[i], t);
}
c.add(b1);
c.add(b2);
}
public static void main(String[] args) {
Console.run(newTrackEvent(), 700, 500);
}
} ///:~
In the MyButton constructor,the button's color is setwith a call to
SetBackground( ). The listeners are allinstalled with simplemethod
calls.
TheTrackEventclasscontains a HashMap to hold the strings
representingthe type of event andJTextFields where informationabout
thatevent is held. Of course,these could have beencreated statically
ratherthan putting them in a HashMap, but I think you'll agreethat it's
a lot easier to use andchange. In particular, if youneed to add or remove
a new type of event in TrackEvent, you simply add or remove a string in
theeventarray--everythingelse happensautomatically.
Whenreport( ) is called it is given the name of the event andthe
parameterstring from the event. It uses the HashMap h in the outer
class to look up the actualJTextFieldassociatedwith that eventname,
andthen places the parameterstring into thatfield.
Thisexample is fun to play withsince you can reallysee what's going on
withthe events in yourprogram.
Chapter13: Creating Windows & Applets
733
img
A catalog of Swing
components
Nowthat you understand layoutmanagers and the eventmodel, you're
ready to see how Swing componentscan be used. This section is a
nonexhaustivetour of the Swing componentsand features thatyou'll
probablyuse most of the time.Each example is intended to be reasonably
small so that you can easilylift the code anduse it in your ownprograms.
Youcan easily see whateach of these examples lookslike while running
by viewing the HTML pages in the downloadable source codefor this
chapter.
Keep in mind:
1.
TheHTML documentation fromjava.sun.comcontainsall of the
Swingclasses and methods (only a few are shownhere).
2.
Because of the naming conventionused for Swing events,it's fairly
easy to guess how to write andinstall a handler for a particular type
of event. Use the lookupprogram ShowAddListeners.javafrom
earlier in this chapter to aid in your investigation of a particular
component.
3.
Whenthings start to getcomplicated you shouldgraduate to a GUI
builder.
Buttons
Swingincludes a number of differenttypes of buttons. Allbuttons, check
boxes,radio buttons, and evenmenu items are inheritedfrom
AbstractButton(which,since menu items areincluded, wouldprobably
havebeen better named"AbstractChooser" or something equallygeneral).
You'llsee the use of menuitems shortly, but thefollowing exampleshows
thevarious types of buttonsavailable:
//: c13:Buttons.java
// Various Swing buttons.
// < applet code=Buttons
734
Thinking in Java
img
//   width=350 height=100></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.plaf.basic.*;
import javax.swing.border.*;
import com.bruceeckel.swing.*;
public class Buttons extends JApplet {
JButton jb = new JButton("JButton");
BasicArrowButton
up = new BasicArrowButton(
BasicArrowButton.NORTH),
down = new BasicArrowButton(
BasicArrowButton.SOUTH),
right = new BasicArrowButton(
BasicArrowButton.EAST),
left = new BasicArrowButton(
BasicArrowButton.WEST);
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(jb);
cp.add(new JToggleButton("JToggleButton"));
cp.add(new JCheckBox("JCheckBox"));
cp.add(new JRadioButton("JRadioButton"));
JPanel jp = new JPanel();
jp.setBorder(newTitledBorder("Directions"));
jp.add(up);
jp.add(down);
jp.add(left);
jp.add(right);
cp.add(jp);
}
public static void main(String[] args) {
Console.run(new Buttons(), 350, 100);
}
} ///:~
Thisbegins with the BasicArrowButtonfrom
javax.swing.plaf.basic, then continues with thevarious specifictypes
Chapter13: Creating Windows & Applets
735
img
of buttons. When you runthe example, you'll seethat the togglebutton
holdsits last position, in or out. But the checkboxes and radiobuttons
behaveidentically to each other,just clicking on or off(they are inherited
fromJToggleButton).
Buttongroups
If you want radio buttons to behave in an "exclusive or"fashion, you must
addthem to a "button group."But, as the example belowdemonstrates,
anyAbstractButtoncan be added to a ButtonGroup.
To avoid repeating a lot of code, this example usesreflection to generate
thegroups of different types of buttons. This is seen in makeBPanel( ),
whichcreates a button group and a JPanel. The second argument to
makeBPanel( ) is an array of String. For each String, a button of the
classrepresented by the firstargument is added to theJPanel:
//: c13:ButtonGroups.java
// Uses reflection to create groups
// of different types of AbstractButton.
// < applet code=ButtonGroups
//   width=500 height=300></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import java.lang.reflect.*;
import com.bruceeckel.swing.*;
public class ButtonGroups extends JApplet {
static String[] ids = {
"June", "Ward", "Beaver",
"Wally", "Eddie", "Lumpy",
};
static JPanel
makeBPanel(Class bClass, String[] ids) {
ButtonGroup bg = new ButtonGroup();
JPanel jp = new JPanel();
String title = bClass.getName();
title = title.substring(
title.lastIndexOf('.') + 1);
736
Thinking in Java
img
jp.setBorder(newTitledBorder(title));
for(int i = 0; i < ids.length; i++) {
AbstractButton ab = new JButton("failed");
try {
// Get the dynamic constructor method
// that takes a String argument:
Constructor ctor = bClass.getConstructor(
new Class[] { String.class });
// Create a new object:
ab = (AbstractButton)ctor.newInstance(
new Object[]{ids[i]});
} catch(Exception ex) {
System.err.println("can't create " +
bClass);
}
bg.add(ab);
jp.add(ab);
}
return jp;
}
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(makeBPanel(JButton.class, ids));
cp.add(makeBPanel(JToggleButton.class, ids));
cp.add(makeBPanel(JCheckBox.class, ids));
cp.add(makeBPanel(JRadioButton.class, ids));
}
public static void main(String[] args) {
Console.run(newButtonGroups(), 500, 300);
}
} ///:~
Thetitle for the border is taken from the name of the class, strippingoff
allthe path information. TheAbstractButtonis initialized to a
JButton thathas the label "Failed" so if you ignore theexception
message,you'll still see theproblem on screen. ThegetConstructor( )
methodproduces a Constructorobjectthat takes the array of arguments
of the types in the Classarraypassed to getConstructor( ). Then all
you do is call newInstance( ), passing it an array of Objectcontaining
youractual arguments--in thiscase, just the Stringfromthe ids array.
Chapter13: Creating Windows & Applets
737
img
Thisadds a little complexity to what is a simple process. To get "exclusive
or"behavior with buttons, youcreate a button group andadd each button
forwhich you want thatbehavior to the group. Whenyou run the
program,you'll see that allthe buttons except JButton exhibitthis
"exclusiveor" behavior.
Icons
Youcan use an Icon inside a JLabelor anything that inheritsfrom
AbstractButton(includingJButton, JCheckBox, JRadioButton,
andthe different kinds of JMenuItem). Using Icons with JLabels is
quitestraightforward (you'll see an example later). Thefollowing example
exploresall the additional waysyou can use Icons with buttons andtheir
descendants.
Youcan use any giffilesyou want, but theones used in this exampleare
part of this book's codedistribution, available at .
To open a file and bring in the image, simply create an ImageIcon and
hand it the file name. Fromthen on, you canuse the resulting Icon in
yourprogram.
Notethat path information is hard-coded into thisexample; you willneed
to change the path to correspond to the location of the image files.
//: c13:Faces.java
// Icon behavior in Jbuttons.
// < applet code=Faces
//   width=250 height=100></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;
public class Faces extends JApplet {
// The following path information is necessary
// to run via an applet directly from the disk:
static String path =
"C:/aaa-TIJ2-distribution/code/c13/";
static Icon[] faces = {
new ImageIcon(path + "face0.gif"),
new ImageIcon(path + "face1.gif"),
738
Thinking in Java
img
new ImageIcon(path + "face2.gif"),
new ImageIcon(path + "face3.gif"),
new ImageIcon(path + "face4.gif"),
};
JButton
jb = new JButton("JButton", faces[3]),
jb2 = new JButton("Disable");
boolean mad = false;
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
jb.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
if(mad) {
jb.setIcon(faces[3]);
mad = false;
} else {
jb.setIcon(faces[0]);
mad = true;
}
jb.setVerticalAlignment(JButton.TOP);
jb.setHorizontalAlignment(JButton.LEFT);
}
});
jb.setRolloverEnabled(true);
jb.setRolloverIcon(faces[1]);
jb.setPressedIcon(faces[2]);
jb.setDisabledIcon(faces[4]);
jb.setToolTipText("Yow!");
cp.add(jb);
jb2.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
if(jb.isEnabled()) {
jb.setEnabled(false);
jb2.setText("Enable");
} else {
jb.setEnabled(true);
jb2.setText("Disable");
}
}
});
Chapter13: Creating Windows & Applets
739
img
cp.add(jb2);
}
public static void main(String[] args) {
Console.run(new Faces(), 400, 200);
}
} ///:~
An Icon can be used in many constructors,but you can alsouse
setIcon( ) to add or change an Icon. This example also showshow a
JButton (orany AbstractButton) can set the variousdifferent sorts of
iconsthat appear when thingshappen to that button: whenit's pressed,
disabled, or "rolled over" (the mousemoves over it withoutclicking).
You'llsee that this givesthe button a nice animatedfeel.
Tool tips
Theprevious example added a "tool tip" to the button.Almost all of the
classesthat you'll be using to create your user interfacesare derived from
JComponent, which contains a methodcalled
setToolTipText(String). So, for virtually anythingyou place on your
form,all you need to do is say(for an object jc of any JComponent-
derivedclass):
jc.setToolTipText("My tip");
andwhen the mouse staysover that JComponent for a predetermined
period of time, a tiny boxcontaining your text willpop up next to the
mouse.
Textfields
Thisexample shows the extrabehavior that JTextFields are capable of:
//: c13:TextFields.java
// Text fields and Java events.
// < applet code=TextFields width=375
// height=125></applet>
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
740
Thinking in Java
img
import com.bruceeckel.swing.*;
public class TextFields extends JApplet {
JButton
b1 = new JButton("Get Text"),
b2 = new JButton("Set Text");
JTextField
t1 = new JTextField(30),
t2 = new JTextField(30),
t3 = new JTextField(30);
String s = new String();
UpperCaseDocument
ucd = new UpperCaseDocument();
public void init() {
t1.setDocument(ucd);
ucd.addDocumentListener(new T1());
b1.addActionListener(new B1());
b2.addActionListener(new B2());
DocumentListener dl = new T1();
t1.addActionListener(new T1A());
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(b1);
cp.add(b2);
cp.add(t1);
cp.add(t2);
cp.add(t3);
}
class T1 implements DocumentListener {
public void changedUpdate(DocumentEvent e){}
public void insertUpdate(DocumentEvent e){
t2.setText(t1.getText());
t3.setText("Text: "+ t1.getText());
}
public void removeUpdate(DocumentEvent e){
t2.setText(t1.getText());
}
}
class T1A implements ActionListener {
private int count = 0;
public void actionPerformed(ActionEvent e) {
Chapter13: Creating Windows & Applets
741
img
t3.setText("t1 Action Event " + count++);
}
}
class B1 implements ActionListener {
public void actionPerformed(ActionEvent e) {
if(t1.getSelectedText() == null)
s = t1.getText();
else
s = t1.getSelectedText();
t1.setEditable(true);
}
}
class B2 implements ActionListener {
public void actionPerformed(ActionEvent e) {
ucd.setUpperCase(false);
t1.setText("Inserted by Button 2: " + s);
ucd.setUpperCase(true);
t1.setEditable(false);
}
}
public static void main(String[] args) {
Console.run(newTextFields(), 375, 125);
}
}
class UpperCaseDocument extends PlainDocument {
boolean upperCase = true;
public void setUpperCase(boolean flag) {
upperCase = flag;
}
public void insertString(int offset,
String string, AttributeSet attributeSet)
throws BadLocationException {
if(upperCase)
string = string.toUpperCase();
super.insertString(offset,
string, attributeSet);
}
} ///:~
742
Thinking in Java
img
TheJTextField t3 is included as a place to reportwhen the action
listenerfor the JTextField t1 is fired. You'll see thatthe action listener
for a JTextFieldis fired only when youpress the "enter"key.
TheJTextField t1 hasseveral listeners attached to it. The T1 listener is a
DocumentListenerthatresponds to any change in the "document" (the
contents of the JTextField, in this case). It automaticallycopies all text
fromt1 intot2. In addition, t1's document is set to a derivedclass of
PlainDocument, called UpperCaseDocument, which forces all
characters to uppercase. It automatically detectsbackspaces andperforms
thedeletion, adjusting thecaret and handlingeverything as youwould
expect.
Borders
JComponent contains a method called setBorder( ), which allows you
to place various interestingborders on any visiblecomponent. The
followingexample demonstrates a number of the different bordersthat
areavailable, using a methodcalled showBorder( ) thatcreates a
JPanelandputs on the border in eachcase. Also, it uses RTTI to find the
name of the border that you'reusing (stripping off allthe path
information),then puts that name in a JLabelin the middle of thepanel:
//: c13:Borders.java
// Different Swing borders.
// < applet code=Borders
//   width=500 height=300></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import com.bruceeckel.swing.*;
public class Borders extends JApplet {
static JPanel showBorder(Border b) {
JPanel jp = new JPanel();
jp.setLayout(newBorderLayout());
String nm = b.getClass().toString();
nm = nm.substring(nm.lastIndexOf('.') + 1);
jp.add(new JLabel(nm, JLabel.CENTER),
Chapter13: Creating Windows & Applets
743
img
BorderLayout.CENTER);
jp.setBorder(b);
return jp;
}
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.setLayout(newGridLayout(2,4));
cp.add(showBorder(newTitledBorder("Title")));
cp.add(showBorder(newEtchedBorder()));
cp.add(showBorder(newLineBorder(Color.blue)));
cp.add(showBorder(
new MatteBorder(5,5,30,30,Color.green)));
cp.add(showBorder(
new BevelBorder(BevelBorder.RAISED)));
cp.add(showBorder(
new SoftBevelBorder(BevelBorder.LOWERED)));
cp.add(showBorder(newCompoundBorder(
new EtchedBorder(),
new LineBorder(Color.red))));
}
public static void main(String[] args) {
Console.run(new Borders(), 500, 300);
}
} ///:~
Youcan also create yourown borders and putthem inside buttons,labels,
etc.--anythingderived from JComponent.
JScrollPanes
Most of the time you'll justwant to let a JScrollPanedo it's job, butyou
canalso control which scrollbars are allowed--vertical,horizontal, both,
or neither:
//: c13:JScrollPanes.java
// Controlling the scrollbars in a JScrollPane.
// < applet code=JScrollPanes width=300 height=725>
// < /applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
744
Thinking in Java
img
import javax.swing.border.*;
import com.bruceeckel.swing.*;
public class JScrollPanes extends JApplet {
JButton
b1 = new JButton("Text Area 1"),
b2 = new JButton("Text Area 2"),
b3 = new JButton("Replace Text"),
b4 = new JButton("Insert Text");
JTextArea
t1 = new JTextArea("t1", 1, 20),
t2 = new JTextArea("t2", 4, 20),
t3 = new JTextArea("t3", 1, 20),
t4 = new JTextArea("t4", 10, 10),
t5 = new JTextArea("t5", 4, 20),
t6 = new JTextArea("t6", 10, 10);
JScrollPane
sp3 = new JScrollPane(t3,
JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),
sp4 = new JScrollPane(t4,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),
sp5 = new JScrollPane(t5,
JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS),
sp6 = new JScrollPane(t6,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
class B1L implements ActionListener {
public void actionPerformed(ActionEvent e) {
t5.append(t1.getText() + "\n");
}
}
class B2L implements ActionListener {
public void actionPerformed(ActionEvent e) {
t2.setText("Inserted by Button 2");
t2.append(": " + t1.getText());
t5.append(t2.getText() + "\n");
}
}
Chapter13: Creating Windows & Applets
745
img
class B3L implements ActionListener {
public void actionPerformed(ActionEvent e) {
String s = " Replacement ";
t2.replaceRange(s, 3, 3 + s.length());
}
}
class B4L implements ActionListener {
public void actionPerformed(ActionEvent e) {
t2.insert(" Inserted ", 10);
}
}
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
// Create Borders for components:
Border brd = BorderFactory.createMatteBorder(
1, 1, 1, 1, Color.black);
t1.setBorder(brd);
t2.setBorder(brd);
sp3.setBorder(brd);
sp4.setBorder(brd);
sp5.setBorder(brd);
sp6.setBorder(brd);
// Initialize listeners and add components:
b1.addActionListener(new B1L());
cp.add(b1);
cp.add(t1);
b2.addActionListener(new B2L());
cp.add(b2);
cp.add(t2);
b3.addActionListener(new B3L());
cp.add(b3);
b4.addActionListener(new B4L());
cp.add(b4);
cp.add(sp3);
cp.add(sp4);
cp.add(sp5);
cp.add(sp6);
}
public static void main(String[] args) {
Console.run(newJScrollPanes(), 300, 725);
746
Thinking in Java
img
}
} ///:~
Usingdifferent arguments in theJScrollPaneconstructorcontrols the
scrollbarsthat are available. Thisexample also dresses things up a bit
usingborders.
A mini-editor
TheJTextPanecontrolprovides a great deal of support for editing,
withoutmuch effort. The followingexample makes very simpleuse of this,
ignoringthe bulk of thefunctionality of theclass:
//: c13:TextPane.java
// The JTextPane control is a little editor.
// < applet code=TextPane width=475 height=425>
// < /applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;
import com.bruceeckel.util.*;
public class TextPane extends JApplet {
JButton b = new JButton("Add Text");
JTextPane tp = new JTextPane();
static Generator sg =
new Arrays2.RandStringGenerator(7);
public void init() {
b.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
for(int i = 1; i < 10; i++)
tp.setText(tp.getText() +
sg.next() + "\n");
}
});
Container cp = getContentPane();
cp.add(new JScrollPane(tp));
cp.add(BorderLayout.SOUTH, b);
}
public static void main(String[] args) {
Chapter13: Creating Windows & Applets
747
img
Console.run(newTextPane(), 475, 425);
}
} ///:~
Thebutton just adds randomlygenerated text. The intent of the
JTextPaneis to allow text to be edited in place, so you will seethat there
is no append( ) method. In this case (admittedly, a poor use of the
capabilities of JTextPane), the text must be captured,modified, and
placedback into the paneusing setText( ).
As mentioned before, thedefault layout behavior of an applet is to use the
BorderLayout. If you add something to thepane without specifyingany
details, it just fills the center of the pane out to theedges. However, if you
specifyone of the surroundingregions (NORTH, SOUTH, EAST, or
WEST) as is done here, thecomponent will fit itselfinto that region--in
thiscase, the button willnest down at the bottom of the screen.
Noticethe built-in features of JTextPane, such as automaticline
wrapping.There are lots of otherfeatures that you canlook up using the
JDKdocumentation.
Check boxes
A check box provides a way to make a single on/off choice; it consists of a
tinybox and a label. Thebox typically holds a little"x" (or someother
indicationthat it is set) or is empty,depending on whether thatitem was
selected.
You'llnormally create a JCheckBoxusing a constructor that takesthe
label as an argument. You can getand set the state,and also get andset
thelabel if you want to read or change it after theJCheckBoxhasbeen
created.
Whenever a JCheckBoxis set or cleared, an eventoccurs, which youcan
capturethe same way you do a button, by using an ActionListener. The
followingexample uses a JTextArea to enumerate all the checkboxes
thathave been checked:
//: c13:CheckBoxes.java
// Using JCheckBoxes.
// < applet code=CheckBoxes width=200 height=200>
748
Thinking in Java
img
// < /applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class CheckBoxes extends JApplet {
JTextArea t = new JTextArea(6, 15);
JCheckBox
cb1 = new JCheckBox("Check Box 1"),
cb2 = new JCheckBox("Check Box 2"),
cb3 = new JCheckBox("Check Box 3");
public void init() {
cb1.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
trace("1", cb1);
}
});
cb2.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
trace("2", cb2);
}
});
cb3.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
trace("3", cb3);
}
});
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(new JScrollPane(t));
cp.add(cb1);
cp.add(cb2);
cp.add(cb3);
}
void trace(String b, JCheckBox cb) {
if(cb.isSelected())
t.append("Box " + b + " Set\n");
else
t.append("Box " + b + " Cleared\n");
}
Chapter13: Creating Windows & Applets
749
img
public static void main(String[] args) {
Console.run(newCheckBoxes(), 200, 200);
}
} ///:~
Thetrace( ) methodsends the name of theselected JCheckBoxandits
currentstate to the JTextArea usingappend( ), so you'll see a
cumulativelist of the checkboxes thatwere selected and whattheir state
is.
Radio buttons
Theconcept of a radio button in GUI programming comes frompre-
electroniccar radios with mechanicalbuttons: when you pushone in, any
otherbutton that was pressedpops out. Thus, it allowsyou to force a
singlechoice among many.
Allyou need to do to set up an associated group of JRadioButtons is to
addthem to a ButtonGroup(youcan have any number of
ButtonGroups on a form). One of thebuttons can optionally haveits
startingstate set to true (usingthe second argument in theconstructor).
If you try to set morethan one radio button to true thenonly the final
oneset will be true.
Here's a simple example of the use of radio buttons. Note thatyou capture
radiobutton events like allothers:
//: c13:RadioButtons.java
// Using JRadioButtons.
// < applet code=RadioButtons
// width=200 height=100> </applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class RadioButtons extends JApplet {
JTextField t = new JTextField(15);
ButtonGroup g = new ButtonGroup();
JRadioButton
rb1 = new JRadioButton("one", false),
750
Thinking in Java
img
rb2 = new JRadioButton("two", false),
rb3 = new JRadioButton("three", false);
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
t.setText("Radio button " +
((JRadioButton)e.getSource()).getText());
}
};
public void init() {
rb1.addActionListener(al);
rb2.addActionListener(al);
rb3.addActionListener(al);
g.add(rb1);g.add(rb2); g.add(rb3);
t.setEditable(false);
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(t);
cp.add(rb1);
cp.add(rb2);
cp.add(rb3);
}
public static void main(String[] args) {
Console.run(newRadioButtons(), 200, 100);
}
} ///:~
To display the state, a textfield is used. This field is set to noneditable
becauseit's used only to displaydata, not to collect it.Thus it is an
alternative to using a JLabel.
Combo boxes (drop-down lists)
Like a group of radio buttons, a drop-down list is a way to force the user
to select only one elementfrom a group of possibilities.However, it's a
morecompact way to accomplishthis, and it's easier to change the
elements of the list withoutsurprising the user. (Youcan change radio
buttonsdynamically, but that tends to be visibly jarring).
Java'sJComboBox box is not like the combobox in Windows, whichlets
youselect from a list or type in your own selection. With a JComboBox
boxyou choose one andonly one element fromthe list. In thefollowing
Chapter13: Creating Windows & Applets
751
img
example,the JComboBox boxstarts with a certain number of entries
andthen new entries areadded to the box when a button is pressed.
//: c13:ComboBoxes.java
// Using drop-down lists.
// < applet code=ComboBoxes
// width=200 height=100> </applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class ComboBoxes extends JApplet {
String[] description = { "Ebullient", "Obtuse",
"Recalcitrant","Brilliant", "Somnescent",
"Timorous", "Florid", "Putrescent" };
JTextField t = new JTextField(15);
JComboBox c = new JComboBox();
JButton b = new JButton("Add items");
int count = 0;
public void init() {
for(int i = 0; i < 4; i++)
c.addItem(description[count++]);
t.setEditable(false);
b.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
if(count < description.length)
c.addItem(description[count++]);
}
});
c.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
t.setText("index: "+ c.getSelectedIndex()
+"
" + ((JComboBox)e.getSource())
.getSelectedItem());
}
});
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(t);
cp.add(c);
752
Thinking in Java
img
cp.add(b);
}
public static void main(String[] args) {
Console.run(newComboBoxes(), 200, 100);
}
} ///:~
TheJTextFielddisplaysthe "selected index," which is the sequence
number of the currently selectedelement, as well as thelabel on the radio
button.
List boxes
Listboxes are significantlydifferent from JComboBox boxes,and not
just in appearance. While a JComboBox boxdrops down whenyou
activateit, a JListoccupiessome fixed number of lines on a screen all the
timeand doesn't change. If youwant to see the items in a list, you simply
callgetSelectedValues( ), whichproduces an array of Stringof the
itemsthat have beenselected.
A JListallowsmultiple selection: if youcontrol-click on more thanone
item(holding down the "control"key while performingadditional mouse
clicks)the original item stayshighlighted and you canselect as many as
youwant. If you select an item,then shift-click on anotheritem, all the
items in the span between thetwo are selected. To remove an item from a
groupyou can control-clickit.
//: c13:List.java
// < applet code=List width=250
// height=375> </applet>
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import com.bruceeckel.swing.*;
public class List extends JApplet {
String[] flavors = { "Chocolate","Strawberry",
"Vanilla Fudge Swirl", "Mint Chip",
"Mocha Almond Fudge", "Rum Raisin",
Chapter13: Creating Windows & Applets
753
img
"Praline Cream", "Mud Pie" };
DefaultListModel lItems=new DefaultListModel();
JList lst = new JList(lItems);
JTextArea t = new JTextArea(flavors.length,20);
JButton b = new JButton("Add Item");
ActionListener bl = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(count < flavors.length) {
lItems.add(0,flavors[count++]);
} else {
// Disable, since there are no more
// flavors left to be added to the List
b.setEnabled(false);
}
}
};
ListSelectionListener ll =
new ListSelectionListener() {
public void valueChanged(
ListSelectionEvent e) {
t.setText("");
Object[] items=lst.getSelectedValues();
for(int i = 0; i < items.length; i++)
t.append(items[i] + "\n");
}
};
int count = 0;
public void init() {
Container cp = getContentPane();
t.setEditable(false);
cp.setLayout(newFlowLayout());
// Create Borders for components:
Border brd = BorderFactory.createMatteBorder(
1, 1, 2, 2, Color.black);
lst.setBorder(brd);
t.setBorder(brd);
// Add the first four items to the List
for(int i = 0; i < 4; i++)
lItems.addElement(flavors[count++]);
// Add items to the Content Pane for Display
cp.add(t);
754
Thinking in Java
img
cp.add(lst);
cp.add(b);
// Register event listeners
lst.addListSelectionListener(ll);
b.addActionListener(bl);
}
public static void main(String[] args) {
Console.run(new List(), 250, 375);
}
} ///:~
Whenyou press the button it adds items to the topof the list (because
addItem( )'s second argument is 0).
Youcan see that bordershave also been added to the lists.
If you just want to put an array of Strings into a JList, there's a much
simplersolution: you pass thearray to the JListconstructor,and it builds
thelist automatically. The onlyreason for using the"list model" in the
aboveexample is so that the listcould be manipulated duringthe
execution of the program.
JLists do not automatically providedirect support forscrolling. Of
course,all you need to do is wrapthe JListin a JScrollPaneandall the
detailsare automatically managedfor you.
Tabbed panes
TheJTabbedPaneallowsyou to create a "tabbeddialog," which hasfile-
foldertabs running across oneedge, and all youhave to do is press a tab
to bring forward a differentdialog.
//: c13:TabbedPane1.java
// Demonstrates the Tabbed Pane.
// < applet code=TabbedPane1
// width=350 height=200> </applet>
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class TabbedPane1 extends JApplet {
Chapter13: Creating Windows & Applets
755
img
String[] flavors = { "Chocolate","Strawberry",
"Vanilla Fudge Swirl", "Mint Chip",
"Mocha Almond Fudge", "Rum Raisin",
"Praline Cream", "Mud Pie" };
JTabbedPane tabs = new JTabbedPane();
JTextField txt = new JTextField(20);
public void init() {
for(int i = 0; i < flavors.length; i++)
tabs.addTab(flavors[i],
new JButton("Tabbed pane " + i));
tabs.addChangeListener(newChangeListener(){
public void stateChanged(ChangeEvent e) {
txt.setText("Tab selected: " +
tabs.getSelectedIndex());
}
});
Container cp = getContentPane();
cp.add(BorderLayout.SOUTH, txt);
cp.add(tabs);
}
public static void main(String[] args) {
Console.run(newTabbedPane1(), 350, 200);
}
} ///:~
In Java, the use of somesort of "tabbed panel"mechanism is quite
importantbecause in applet programmingthe use of pop-up dialogs is
discouraged by automatically adding a littlewarning to any dialogthat
pops up out of an applet.
Whenyou run the programyou'll see that theJTabbedPane
automaticallystacks the tabs if thereare too many of them to fit on one
row.You can see this by resizing the window whenyou run theprogram
fromthe console commandline.
Message boxes
Windowingenvironments commonly contain a standard set of message
boxesthat allow you to quicklypost information to the user or to capture
informationfrom the user. In Swing,these message boxes arecontained
in JOptionPane. You have many differentpossibilities (somequite
756
Thinking in Java
img
sophisticated),but the ones you'llmost commonly use areprobably the
messagedialog and confirmationdialog, invoked using thestatic
JOptionPane.showMessageDialog( ) andJOptionPane.
showConfirmDialog( ). The following example shows a subset of the
messageboxes available withJOptionPane:
//: c13:MessageBoxes.java
// Demonstrates JoptionPane.
// < applet code=MessageBoxes
// width=200 height=150> </applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class MessageBoxes extends JApplet {
JButton[] b = { new JButton("Alert"),
new JButton("Yes/No"), new JButton("Color"),
new JButton("Input"), new JButton("3 Vals")
};
JTextField txt = new JTextField(15);
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e){
String id =
((JButton)e.getSource()).getText();
if(id.equals("Alert"))
JOptionPane.showMessageDialog(null,
"There's a bug on you!", "Hey!",
JOptionPane.ERROR_MESSAGE);
else if(id.equals("Yes/No"))
JOptionPane.showConfirmDialog(null,
"or no", "choose yes",
JOptionPane.YES_NO_OPTION);
else if(id.equals("Color")) {
Object[] options = { "Red", "Green" };
int sel = JOptionPane.showOptionDialog(
null, "Choose a Color!", "Warning",
JOptionPane.DEFAULT_OPTION,
JOptionPane.WARNING_MESSAGE, null,
options, options[0]);
if(sel != JOptionPane.CLOSED_OPTION)
Chapter13: Creating Windows & Applets
757
img
txt.setText(
"Color Selected: " + options[sel]);
} else if(id.equals("Input")) {
String val = JOptionPane.showInputDialog(
"How many fingers do you see?");
txt.setText(val);
} else if(id.equals("3 Vals")) {
Object[] selections = {
"First", "Second", "Third" };
Object val = JOptionPane.showInputDialog(
null, "Choose one", "Input",
JOptionPane.INFORMATION_MESSAGE,
null, selections, selections[0]);
if(val != null)
txt.setText(
val.toString());
}
}
};
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
for(int i = 0; i < b.length; i++) {
b[i].addActionListener(al);
cp.add(b[i]);
}
cp.add(txt);
}
public static void main(String[] args) {
Console.run(newMessageBoxes(), 200, 200);
}
} ///:~
To be able to write a singleActionListener, I've used thesomewhat
riskyapproach of checking theStringlabels on the buttons. Theproblem
withthis is that it's easy to get the label a littlebit wrong, typically in
capitalization,and this bug can be hard to spot.
Notethat showOptionDialog( ) andshowInputDialog( ) provide
returnobjects that contain thevalue entered by theuser.
758
Thinking in Java
img
Menus
Eachcomponent capable of holding a menu, including JApplet,
JFrame, JDialog, and their descendants, has a setJMenuBar( )
methodthat accepts a JMenuBar (youcan have only oneJMenuBar on
a particular component). Youadd JMenus to the JMenuBar, and
JMenuItems to the JMenus. Each JMenuItem canhave an
ActionListenerattached to it, to be fired when thatmenu item is
selected.
Unlike a system that usesresources, with Java andSwing you musthand
assembleall the menus in sourcecode. Here is a very simplemenu
example:
//: c13:SimpleMenus.java
// < applet code=SimpleMenus
// width=200 height=75> </applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class SimpleMenus extends JApplet {
JTextField t = new JTextField(15);
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e){
t.setText(
((JMenuItem)e.getSource()).getText());
}
};
JMenu[] menus = { new JMenu("Winken"),
new JMenu("Blinken"), new JMenu("Nod") };
JMenuItem[] items = {
new JMenuItem("Fee"), new JMenuItem("Fi"),
new JMenuItem("Fo"),  new JMenuItem("Zip"),
new JMenuItem("Zap"), new JMenuItem("Zot"),
new JMenuItem("Olly"), new JMenuItem("Oxen"),
new JMenuItem("Free") };
public void init() {
for(int i = 0; i < items.length; i++) {
items[i].addActionListener(al);
Chapter13: Creating Windows & Applets
759
img
menus[i%3].add(items[i]);
}
JMenuBar mb = new JMenuBar();
for(int i = 0; i < menus.length; i++)
mb.add(menus[i]);
setJMenuBar(mb);
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(t);
}
public static void main(String[] args) {
Console.run(newSimpleMenus(), 200, 75);
}
} ///:~
Theuse of the modulus operator in "i%3" distributes the menuitems
amongthe three JMenus. Each JMenuItem musthave an
ActionListenerattached to it; here, the sameActionListeneris used
everywherebut you'll usually need an individual one foreach
JMenuItem.
JMenuItem inheritsAbstractButton, so it has somebuttonlike
behaviors. By itself, it provides an itemthat can be placed on a drop-down
menu.There are also threetypes inherited fromJMenuItem: JMenu to
holdother JMenuItems (so you can havecascading menus),
JCheckBoxMenuItem, which produces a checkmark to indicate
whetherthat menu item is selected,and JRadioButtonMenuItem,
whichcontains a radiobutton.
As a more sophisticated example,here are the icecream flavors again,
used to create menus. Thisexample also shows cascadingmenus,
keyboardmnemonics, JCheckBoxMenuItems, and the way youcan
dynamicallychange menus:
//: c13:Menus.java
// Submenus, checkbox menu items, swapping menus,
// mnemonics (shortcuts) and action commands.
// < applet code=Menus width=300
// height=100> </applet>
import javax.swing.*;
import java.awt.*;
760
Thinking in Java
img
import java.awt.event.*;
import com.bruceeckel.swing.*;
public class Menus extends JApplet {
String[] flavors = { "Chocolate","Strawberry",
"Vanilla Fudge Swirl", "Mint Chip",
"Mocha Almond Fudge", "Rum Raisin",
"Praline Cream", "Mud Pie" };
JTextField t = new JTextField("No flavor", 30);
JMenuBar mb1 = new JMenuBar();
JMenu
f = new JMenu("File"),
m = new JMenu("Flavors"),
s = new JMenu("Safety");
// Alternative approach:
JCheckBoxMenuItem[] safety = {
new JCheckBoxMenuItem("Guard"),
new JCheckBoxMenuItem("Hide")
};
JMenuItem[] file = {
new JMenuItem("Open"),
};
// A second menu bar to swap to:
JMenuBar mb2 = new JMenuBar();
JMenu fooBar = new JMenu("fooBar");
JMenuItem[] other = {
// Adding a menu shortcut (mnemonic) is very
// simple, but only JMenuItems can have them
// in their constructors:
new JMenuItem("Foo", KeyEvent.VK_F),
new JMenuItem("Bar", KeyEvent.VK_A),
// No shortcut:
new JMenuItem("Baz"),
};
JButton b = new JButton("Swap Menus");
class BL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JMenuBar m = getJMenuBar();
setJMenuBar(m == mb1 ? mb2 : mb1);
validate(); // Refresh the frame
}
Chapter13: Creating Windows & Applets
761
img
}
class ML implements ActionListener {
public void actionPerformed(ActionEvent e) {
JMenuItem target = (JMenuItem)e.getSource();
String actionCommand =
target.getActionCommand();
if(actionCommand.equals("Open")) {
String s = t.getText();
boolean chosen = false;
for(int i = 0; i < flavors.length; i++)
if(s.equals(flavors[i])) chosen = true;
if(!chosen)
t.setText("Choose a flavor first!");
else
t.setText("Opening "+ s +". Mmm, mm!");
}
}
}
class FL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JMenuItem target = (JMenuItem)e.getSource();
t.setText(target.getText());
}
}
// Alternatively, you can create a different
// class for each different MenuItem. Then you
// Don't have to figure out which one it is:
class FooL implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.setText("Fooselected");
}
}
class BarL implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.setText("Barselected");
}
}
class BazL implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.setText("Bazselected");
}
762
Thinking in Java
img
}
class CMIL implements ItemListener {
public void itemStateChanged(ItemEvent e) {
JCheckBoxMenuItem target =
(JCheckBoxMenuItem)e.getSource();
String actionCommand =
target.getActionCommand();
if(actionCommand.equals("Guard"))
t.setText("Guard the Ice Cream! " +
"Guarding is " + target.getState());
else if(actionCommand.equals("Hide"))
t.setText("Hide the Ice Cream! " +
"Is it cold? " + target.getState());
}
}
public void init() {
ML ml = new ML();
CMIL cmil = new CMIL();
safety[0].setActionCommand("Guard");
safety[0].setMnemonic(KeyEvent.VK_G);
safety[0].addItemListener(cmil);
safety[1].setActionCommand("Hide");
safety[0].setMnemonic(KeyEvent.VK_H);
safety[1].addItemListener(cmil);
other[0].addActionListener(new FooL());
other[1].addActionListener(new BarL());
other[2].addActionListener(new BazL());
FL fl = new FL();
for(int i = 0; i < flavors.length; i++) {
JMenuItem mi = new JMenuItem(flavors[i]);
mi.addActionListener(fl);
m.add(mi);
// Add separators at intervals:
if((i+1) % 3 == 0)
m.addSeparator();
}
for(int i = 0; i < safety.length; i++)
s.add(safety[i]);
s.setMnemonic(KeyEvent.VK_A);
f.add(s);
f.setMnemonic(KeyEvent.VK_F);
Chapter13: Creating Windows & Applets
763
img
for(int i = 0; i < file.length; i++) {
file[i].addActionListener(fl);
f.add(file[i]);
}
mb1.add(f);
mb1.add(m);
setJMenuBar(mb1);
t.setEditable(false);
Container cp = getContentPane();
cp.add(t, BorderLayout.CENTER);
// Set up the system for swapping menus:
b.addActionListener(new BL());
b.setMnemonic(KeyEvent.VK_S);
cp.add(b, BorderLayout.NORTH);
for(int i = 0; i < other.length; i++)
fooBar.add(other[i]);
fooBar.setMnemonic(KeyEvent.VK_B);
mb2.add(fooBar);
}
public static void main(String[] args) {
Console.run(new Menus(), 300, 100);
}
} ///:~
In this program I placed themenu items into arraysand then stepped
througheach array calling add( ) foreach JMenuItem. This makes
adding or subtracting a menu itemsomewhat lesstedious.
Thisprogram creates not onebut two JMenuBars to demonstrate that
menubars can be actively swappedwhile the program is running. You can
seehow a JMenuBar is made up of JMenus, and each JMenu is made
up of JMenuItems, JCheckBoxMenuItems, or even other JMenus
(whichproduce submenus). When a JMenuBar is assembled it can be
installedinto the current programwith the setJMenuBar( ) method.
Notethat when the button is pressed, it checks to seewhich menu is
currentlyinstalled by calling getJMenuBar( ), then it puts theother
menubar in its place.
Whentesting for "Open," noticethat spelling andcapitalization are
critical,but Java signals no error if there is no match with"Open." This
kind of string comparison is a source of programming errors.
764
Thinking in Java
img
Thechecking and unchecking of the menu items is takencare of
automatically.The code handling theJCheckBoxMenuItems shows
twodifferent ways to determinewhat was checked: stringmatching
(which, as mentioned above, isn't a very safe approach althoughyou'll see
it used) and matching on theevent target object. As shown, the
getState( ) methodcan be used to reveal thestate. You can alsochange
thestate of a JCheckBoxMenuItemwithsetState( ).
Theevents for menus are a bit inconsistent and canlead to confusion:
JMenuItems use ActionListeners, but JCheckboxMenuItems use
ItemListeners. The JMenu objectscan also support ActionListeners,
butthat's not usually helpful. In general, you'll attachlisteners to each
JMenuItem, JCheckBoxMenuItem, or JRadioButtonMenuItem,
butthe example shows ItemListeners and ActionListeners attached
to the various menucomponents.
Swingsupports mnemonics, or "keyboardshortcuts," so you canselect
anythingderived from AbstractButton(button,menu item, etc.)using
thekeyboard instead of themouse. These are quitesimple: for
JMenuItem youcan use the overloadedconstructor that takes as a
secondargument the identifier forthe key. However,most
AbstractButtons do not have constructorslike this so the moregeneral
way to solve the problem is to use the setMnemonic( ) method.The
exampleabove adds mnemonics to thebutton and some of themenu
items;shortcut indicators automaticallyappear on thecomponents.
Youcan also see theuse of setActionCommand( ). This seems a bit
strangebecause in each case the"action command" is exactlythe same as
thelabel on the menu component.Why not just usethe label instead of
thisalternative string? Theproblem is internationalization. If youretarget
thisprogram to another language,you want to change onlythe label in the
menu,and not change thecode (which would no doubtintroduce new
errors). So to make this easy forcode that checks thetext stringassociated
with a menu component, the"action command" can be immutable while
themenu label can change.All the code workswith the "action
command," so it's unaffected by changes to the menu labels. Notethat in
thisprogram, not all themenu components are examinedfor their action
commands, so those that aren't don'thave their action commandset.
Chapter13: Creating Windows & Applets
765
img
Thebulk of the work happens in the listeners. BL performsthe
JMenuBar swapping. In ML, the "figure out whorang" approach is
taken by getting the source of theActionEventandcasting it to a
JMenuItem, then getting the actioncommand string to pass it through a
cascadedif statement.
TheFL listener is simple even though it'shandling all the differentflavors
in the flavor menu. Thisapproach is useful if youhave enoughsimplicity
in your logic, but in general,you'll want to take theapproach used with
FooL, BarL, andBazL, in which they are eachattached to only a single
menucomponent so no extra detectionlogic is necessary and youknow
exactlywho called the listener.Even with the profusion of classes
generatedthis way, the codeinside tends to be smallerand the process is
morefoolproof.
Youcan see that menucode quickly getslong-winded and messy. This is
anothercase where the use of a GUI builder is the appropriatesolution. A
goodtool will also handlethe maintenance of themenus.
Pop-up menus
Themost straightforward way to implement a JPopupMenu is to create
an inner class that extendsMouseAdapter, then add an object of that
innerclass to each component thatyou want to produce pop-upbehavior:
//: c13:Popup.java
// Creating popup menus with Swing.
// < applet code=Popup
//   width=300 height=200></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;
public class Popup extends JApplet {
JPopupMenu popup = new JPopupMenu();
JTextField t = new JTextField(10);
public void init() {
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(t);
766
Thinking in Java
img
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e){
t.setText(
((JMenuItem)e.getSource()).getText());
}
};
JMenuItem m = new JMenuItem("Hither");
m.addActionListener(al);
popup.add(m);
m = new JMenuItem("Yon");
m.addActionListener(al);
popup.add(m);
m = new JMenuItem("Afar");
m.addActionListener(al);
popup.add(m);
popup.addSeparator();
m = new JMenuItem("Stay Here");
m.addActionListener(al);
popup.add(m);
PopupListener pl = new PopupListener();
addMouseListener(pl);
t.addMouseListener(pl);
}
class PopupListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if(e.isPopupTrigger()) {
popup.show(
e.getComponent(), e.getX(), e.getY());
}
}
}
public static void main(String[] args) {
Console.run(new Popup(), 300, 200);
}
} ///:~
Chapter13: Creating Windows & Applets
767
img
Thesame ActionListeneris added to each JMenuItem, so that it
fetchesthe text from themenu label and inserts it into the JTextField.
Drawing
In a good GUI framework, drawingshould be reasonably easy--and it is,
in the Swing library. Theproblem with any drawingexample is that the
calculationsthat determine where things go are typically a lotmore
complicatedthat the calls to thedrawing routines, and thesecalculations
areoften mixed together withthe drawing calls so it canseem that the
interface is more complicated than it actually is.
Forsimplicity, consider theproblem of representing data on the screen--
here,the data will be provided by the built-in Math.sin( ) methodwhich
is a mathematical sine function. To make things a little moreinteresting,
and to further demonstrate howeasy it is to use Swingcomponents, a
sliderwill be placed at the bottom of the form to dynamicallycontrol the
number of sine wave cycles thatare displayed. In addition, if you resize
thewindow, you'll see thatthe sine wave refitsitself to the newwindow
size.
Althoughany JComponent may be painted and thus used as a canvas, if
youjust want a straightforwarddrawing surface you willtypically inherit
from a JPanel. The only method youneed to override is
paintComponent( ), which is called wheneverthat component must be
repainted(you normally don't need to worry about this, as thedecision is
managed by Swing). When it is called,Swing passes a Graphicsobject to
themethod, and you canthen use this object to draw or paint on the
surface.
In the following example, allthe intelligence concerningpainting is in the
SineDraw class;the SineWave classsimply configures theprogram
andthe slider control. InsideSineDraw, the setCycles( ) method
provides a hook to allow anotherobject--the slider control, in this case--
to control the number of cycles.
//: c13:SineWave.java
// Drawing with Swing, using a JSlider.
// < applet code=SineWave
//   width=700 height=400></applet>
768
Thinking in Java
img
import
javax.swing.*;
import
javax.swing.event.*;
import
java.awt.*;
import
com.bruceeckel.swing.*;
class SineDraw extends JPanel {
static final int SCALEFACTOR = 200;
int cycles;
int points;
double[] sines;
int[] pts;
SineDraw() { setCycles(5); }
public void setCycles(int newCycles) {
cycles = newCycles;
points = SCALEFACTOR * cycles * 2;
sines = new double[points];
pts = new int[points];
for(int i = 0; i < points; i++) {
double radians = (Math.PI/SCALEFACTOR) * i;
sines[i] = Math.sin(radians);
}
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int maxWidth = getWidth();
double hstep = (double)maxWidth/(double)points;
int maxHeight = getHeight();
for(int i = 0; i < points; i++)
pts[i] = (int)(sines[i] * maxHeight/2 * .95
+ maxHeight/2);
g.setColor(Color.red);
for(int i = 1; i < points; i++) {
int x1 = (int)((i - 1) * hstep);
int x2 = (int)(i * hstep);
int y1 = pts[i-1];
int y2 = pts[i];
g.drawLine(x1, y1, x2, y2);
}
}
}
Chapter13: Creating Windows & Applets
769
img
public class SineWave extends JApplet {
SineDraw sines = new SineDraw();
JSlider cycles = new JSlider(1, 30, 5);
public void init() {
Container cp = getContentPane();
cp.add(sines);
cycles.addChangeListener(newChangeListener(){
public void stateChanged(ChangeEvent e) {
sines.setCycles(
((JSlider)e.getSource()).getValue());
}
});
cp.add(BorderLayout.SOUTH, cycles);
}
public static void main(String[] args) {
Console.run(newSineWave(), 700, 400);
}
} ///:~
All of the data members andarrays are used in thecalculation of thesine
wavepoints: cyclesindicatesthe number of complete sinewaves desired,
pointscontainsthe total number of pointsthat will be graphed,sines
containsthe sine function values,and ptscontainsthe y-coordinates of
thepoints that will be drawn on the JPanel. The setCycles( ) method
createsthe arrays according to thenumber of points needed andfills the
sinesarraywith numbers. By callingrepaint( ) , setCycles( ) forces
paintComponent( ) to be called so the rest of thecalculation and
redrawwill take place.
Thefirst thing you must do when you override paintComponent( ) is to
callthe base-class version of the method. Then youare free to do
whateveryou like; normally, thismeans using the Graphicsmethods
thatyou can find in thedocumentation for java.awt.Graphics(inthe
HTMLdocumentation from java.sun.com) to draw and paint pixelsonto
theJPanel. Here, you can seethat almost all thecode is involved in
performingthe calculations; the onlytwo method calls thatactually
manipulatethe screen are setColor( ) anddrawLine( ). You will
probablyhave a similar experiencewhen creating your ownprogram that
770
Thinking in Java
img
displaysgraphical data--you'll spendmost of your time figuringout what
it is you want to draw, butthe actual drawing processwill be quite simple.
When I created this program, thebulk of my time was spent in getting the
sinewave to display. Once I didthat, I thought it would be nice to be able
to dynamically change thenumber of cycles. My programming
experienceswhen trying to do suchthings in other languagesmade me a
bitreluctant to try this, but it turned out to be theeasiest part of the
project. I created a JSlider(thearguments are the left-mostvalue of the
JSlider, the right-most value, andthe starting value,respectively, but
thereare other constructors as well) and dropped it intothe JApplet.
Then I looked at the HTMLdocumentation and noticedthat the only
listenerwas the addChangeListener, which was triggered wheneverthe
sliderwas changed enough for it to produce a different value.The only
methodfor this was theobviously named stateChanged( ), which
provided a ChangeEventobject so that I could lookbackward to the
source of the change and findthe new value. By callingthe sinesobject's
setCycles( ), the new value wasincorporated and theJPanelredrawn.
In general, you will findthat most of your Swingproblems can be solved
by following a similar process,and you'll find thatit's generally quite
simple,even if you haven't used a particular componentbefore.
If your problem is morecomplex, there are othermore sophisticated
alternativesfor drawing, includingthird-party JavaBeanscomponents
andthe Java 2D API. Thesesolutions are beyond thescope of this book,
butyou should look them up if your drawing code becomestoo onerous.
Dialog Boxes
A dialog box is a window thatpops up out of anotherwindow. Its purpose
is to deal with some specificissue without cluttering theoriginal window
withthose details. Dialog boxesare heavily used in windowed
programmingenvironments, but lessfrequently used in applets.
To create a dialog box, youinherit from JDialog, which is just another
kind of Window, like a JFrame. A JDialoghas a layout manager
(whichdefaults to BorderLayout) and you add eventlisteners to deal
withevents. One significantdifference when windowClosing( ) is called
is that you don't want to shut down the application.Instead, you release
Chapter13: Creating Windows & Applets
771
img
theresources used by thedialog's window by callingdispose( ). Here's a
verysimple example:
//: c13:Dialogs.java
// Creating and using Dialog Boxes.
// < applet code=Dialogs width=125 height=75>
// < /applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
class MyDialog extends JDialog {
public MyDialog(JFrame parent) {
super(parent, "My dialog", true);
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(new JLabel("Here is my dialog"));
JButton ok = new JButton("OK");
ok.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
dispose(); // Closes the dialog
}
});
cp.add(ok);
setSize(150,125);
}
}
public class Dialogs extends JApplet {
JButton b1 = new JButton("Dialog Box");
MyDialog dlg = new MyDialog(null);
public void init() {
b1.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
dlg.show();
}
});
getContentPane().add(b1);
}
public static void main(String[] args) {
772
Thinking in Java
img
Console.run(new Dialogs(), 125, 75);
}
} ///:~
Oncethe JDialogis created, the show( ) methodmust be called to
displayand activate it. Forthe dialog to close, it mustcall dispose( ).
You'llsee that anything thatpops up out of an applet,including dialog
boxes, is "untrusted." That is, youget a warning in the windowthat's been
poppedup. This is because, in theory, it would be possible to fool the user
intothinking that they'redealing with a regularnative application and to
getthem to type in their creditcard number, which thengoes across the
Web. An applet is always attached to a Web page and visiblewithin your
Webbrowser, while a dialog box is detached--so in theory, it could be
possible. As a result it is not so common to see an applet that uses a dialog
box.
Thefollowing example is morecomplex; the dialog box is made up of a
grid(using GridLayout) of a special kind of buttonthat is defined here
as class ToeButton. This button draws a framearound itself and,
depending on its state, a blank, an "x," or an "o" in themiddle. It starts
outblank, and then depending on whose turn it is, changes to an "x" or an
"o."However, it will also flipback and forth between"x" and "o" whenyou
click on the button. (This makesthe tic-tac-toe concept onlyslightly more
annoyingthan it already is.) In addition, the dialog boxcan be set up for
anynumber of rows and columns by changing numbers in themain
applicationwindow.
//: c13:TicTacToe.java
// Demonstration of dialog boxes
// and creating your own components.
// < applet code=TicTacToe
//   width=200 height=100></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;
public class TicTacToe extends JApplet {
JTextField
rows = new JTextField("3"),
Chapter13: Creating Windows & Applets
773
img
cols = new JTextField("3");
static final int BLANK = 0, XX = 1, OO = 2;
class ToeDialog extends JDialog {
int turn = XX; // Start with x's turn
// w = number of cells wide
// h = number of cells high
public ToeDialog(int w, int h) {
setTitle("The game itself");
Container cp = getContentPane();
cp.setLayout(newGridLayout(w, h));
for(int i = 0; i < w * h; i++)
cp.add(new ToeButton());
setSize(w * 50, h * 50);
// JDK 1.3 close dialog:
//#setDefaultCloseOperation(
//#  DISPOSE_ON_CLOSE);
// JDK 1.2 close dialog:
addWindowListener(newWindowAdapter() {
public void windowClosing(WindowEvent e){
dispose();
}
});
}
class ToeButton extends JPanel {
int state = BLANK;
public ToeButton() {
addMouseListener(new ML());
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int x1 = 0;
int y1 = 0;
int x2 = getSize().width - 1;
int y2 = getSize().height - 1;
g.drawRect(x1, y1, x2, y2);
x1 = x2/4;
y1 = y2/4;
int wide = x2/2;
int high = y2/2;
if(state == XX) {
g.drawLine(x1, y1,
774
Thinking in Java
img
x1 + wide, y1 + high);
g.drawLine(x1, y1 + high,
x1 + wide, y1);
}
if(state == OO) {
g.drawOval(x1, y1,
x1 + wide/2, y1 + high/2);
}
}
class ML extends MouseAdapter {
public void mousePressed(MouseEvent e) {
if(state == BLANK) {
state = turn;
turn = (turn == XX ? OO : XX);
}
else
state = (state == XX ? OO : XX);
repaint();
}
}
}
}
class BL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JDialog d = new ToeDialog(
Integer.parseInt(rows.getText()),
Integer.parseInt(cols.getText()));
d.setVisible(true);
}
}
public void init() {
JPanel p = new JPanel();
p.setLayout(newGridLayout(2,2));
p.add(new JLabel("Rows", JLabel.CENTER));
p.add(rows);
p.add(new JLabel("Columns", JLabel.CENTER));
p.add(cols);
Container cp = getContentPane();
cp.add(p, BorderLayout.NORTH);
JButton b = new JButton("go");
b.addActionListener(new BL());
Chapter13: Creating Windows & Applets
775
img
cp.add(b, BorderLayout.SOUTH);
}
public static void main(String[] args) {
Console.run(newTicTacToe(), 200, 100);
}
} ///:~
Becausestatics can only be at the outerlevel of the class, innerclasses
cannothave staticdata or staticinnerclasses.
ThepaintComponent( ) methoddraws the square aroundthe panel,
andthe "x" or the "o."This is full of tediouscalculations, butit's
straightforward.
A mouse click is captured by the MouseListener, which first checks to
see if the panel has anythingwritten on it. If not, theparent window is
queried to find out whose turn it is and that is used to establish the state
of the ToeButton. Via the inner classmechanism, the ToeButtonthen
reachesback into the parentand changes the turn. If the button is already
displaying an "x" or an "o" then that is flopped. You can see in these
calculationsthe convenient use of theternary if-else described in Chapter
3. After a state change, theToeButtonis repainted.
Theconstructor for ToeDialogis quite simple: it adds into a
GridLayout as many buttons as you request,then resizes it for 50 pixels
on a side for eachbutton.
TicTacToesets up the whole application by creating the JTextFields
(forinputting the rows andcolumns of the button grid)and the "go"
buttonwith its ActionListener. When the button is pressed,the data in
theJTextFields must be fetched, and, sincethey are in Stringform,
turnedinto ints using the staticInteger.parseInt( ) method.
Filedialogs
Someoperating systems have a number of special built-indialog boxes to
handlethe selection of things such as fonts, colors, printers,and the like.
Virtuallyall graphical operatingsystems support the openingand saving
of files, however, and so Java's JFileChooserencapsulatesthese for easy
use.
776
Thinking in Java
img
Thefollowing application exercisestwo forms of JFileChooserdialogs,
onefor opening and onefor saving. Most of thecode should by now be
familiar,and all the interestingactivities happen in theaction listeners for
thetwo different buttonclicks:
//: c13:FileChooserTest.java
// Demonstration of File dialog boxes.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;
public class FileChooserTest extends JFrame {
JTextField
filename = new JTextField(),
dir = new JTextField();
JButton
open = new JButton("Open"),
save = new JButton("Save");
public FileChooserTest() {
JPanel p = new JPanel();
open.addActionListener(new OpenL());
p.add(open);
save.addActionListener(new SaveL());
p.add(save);
Container cp = getContentPane();
cp.add(p, BorderLayout.SOUTH);
dir.setEditable(false);
filename.setEditable(false);
p = new JPanel();
p.setLayout(newGridLayout(2,1));
p.add(filename);
p.add(dir);
cp.add(p, BorderLayout.NORTH);
}
class OpenL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser c = new JFileChooser();
// Demonstrate "Open" dialog:
int rVal =
c.showOpenDialog(FileChooserTest.this);
Chapter13: Creating Windows & Applets
777
img
if(rVal == JFileChooser.APPROVE_OPTION) {
filename.setText(
c.getSelectedFile().getName());
dir.setText(
c.getCurrentDirectory().toString());
}
if(rVal == JFileChooser.CANCEL_OPTION) {
filename.setText("You pressed cancel");
dir.setText("");
}
}
}
class SaveL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser c = new JFileChooser();
// Demonstrate "Save" dialog:
int rVal =
c.showSaveDialog(FileChooserTest.this);
if(rVal == JFileChooser.APPROVE_OPTION) {
filename.setText(
c.getSelectedFile().getName());
dir.setText(
c.getCurrentDirectory().toString());
}
if(rVal == JFileChooser.CANCEL_OPTION) {
filename.setText("You pressed cancel");
dir.setText("");
}
}
}
public static void main(String[] args) {
Console.run(newFileChooserTest(), 250, 110);
}
} ///:~
Notethat there are manyvariations you can apply to JFileChooser,
includingfilters to narrow the filenames that you willallow.
For an "open file" dialog, youcall showOpenDialog( ), and for a "save
file"dialog you call showSaveDialog( ). These commands don'treturn
untilthe dialog is closed. TheJFileChooserobjectstill exists, so youcan
778
Thinking in Java
img
readdata from it. Themethods getSelectedFile( ) and
getCurrentDirectory( ) aretwo ways you caninterrogate the results of
theoperation. If these returnnull it means the user canceledout of the
dialog.
HTML on Swing components
Anycomponent that can taketext can also takeHTML text, which it will
reformataccording to HTML rules.This means you canvery easily add
fancytext to a Swing component.For example,
//: c13:HTMLButton.java
// Putting HTML text on Swing components.
// < applet code=HTMLButton width=200 height=500>
// < /applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class HTMLButton extends JApplet {
JButton b = new JButton("<html><b><font size=+2>" +
"<center>Hello!<br><i>Press me now!");
public void init() {
b.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
getContentPane().add(newJLabel("<html>"+
"<i><font size=+4>Kapow!"));
// Force a re-layout to
// include the new label:
validate();
}
});
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
cp.add(b);
}
public static void main(String[] args) {
Console.run(newHTMLButton(), 200, 500);
}
} ///:~
Chapter13: Creating Windows & Applets
779
img
Youmust start the textwith "<html>," andthen you can usenormal
HTMLtags. Note that youare not forced to includethe normal closing
tags.
TheActionListeneradds a new JLabelto the form, which alsocontains
HTMLtext. However, this label is not added during init( ) so you must
callthe container's validate( ) method in order to force a re-layout of the
components(and thus the display of the new label).
Youcan also use HTMLtext for JTabbedPane, JMenuItem,
JToolTip, JRadioButtonandJCheckBox.
Sliders and progress bars
A slider (which has alreadybeen used in the sinewave example) allows
theuser to input data by moving a point back and forth,which is intuitive
in some situations (volumecontrols, for example). A progress bar displays
data in a relative fashion from"full" to "empty" so theuser gets a
perspective. My favorite example forthese is to simply hook theslider to
theprogress bar so when youmove the slider theprogress bar changes
accordingly:
//: c13:Progress.java
// Using progress bars and sliders.
// < applet code=Progress
//   width=300 height=200></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;
import javax.swing.border.*;
import com.bruceeckel.swing.*;
public class Progress extends JApplet {
JProgressBar pb = new JProgressBar();
JSlider sb =
new JSlider(JSlider.HORIZONTAL, 0, 100, 60);
public void init() {
Container cp = getContentPane();
cp.setLayout(newGridLayout(2,1));
cp.add(pb);
780
Thinking in Java
img
sb.setValue(0);
sb.setPaintTicks(true);
sb.setMajorTickSpacing(20);
sb.setMinorTickSpacing(5);
sb.setBorder(newTitledBorder("Slide Me"));
pb.setModel(sb.getModel()); // Share model
cp.add(sb);
}
public static void main(String[] args) {
Console.run(newProgress(), 300, 200);
}
} ///:~
Thekey to hooking the twocomponents together is in sharingtheir
model, in the line:
pb.setModel(sb.getModel());
Of course, you could alsocontrol the two using a listener, but this is more
straightforwardfor simplesituations.
TheJProgressBaris fairly straightforward, butthe JSliderhas a lot of
options,such as the orientation andmajor and minor tickmarks. Notice
howstraightforward it is to add a titledborder.
Trees
Using a JTreecan be as simple as saying:
add(new JTree(
new Object[] {"this", "that", "other"}));
Thisdisplays a primitive tree.The API for trees is vast, however--certainly
one of the largest in Swing. It appears that you can do just about anything
withtrees, but moresophisticated tasks mightrequire quite a bit of
researchand experimentation.
Fortunately,there is a middle groundprovided in the library:the
"default"tree components, whichgenerally do what you need. So most of
thetime you can usethese components, and only in special cases willyou
need to delve in and understandtrees more deeply.
Chapter13: Creating Windows & Applets
781
img
Thefollowing example uses the"default" tree components to display a
tree in an applet. When you pressthe button, a new subtree is added
underthe currently selected node(if no node is selected, theroot node is
used):
//: c13:Trees.java
// Simple Swing tree example. Trees can
// be made vastly more complex than this.
// < applet code=Trees
//   width=250 height=250></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.tree.*;
import com.bruceeckel.swing.*;
// Takes an array of Strings and makes the first
// element a node and the rest leaves:
class Branch {
DefaultMutableTreeNode r;
public Branch(String[] data) {
r = new DefaultMutableTreeNode(data[0]);
for(int i = 1; i < data.length; i++)
r.add(new DefaultMutableTreeNode(data[i]));
}
public DefaultMutableTreeNode node() {
return r;
}
}
public class Trees extends JApplet {
String[][] data = {
{ "Colors", "Red", "Blue", "Green" },
{ "Flavors", "Tart", "Sweet", "Bland" },
{ "Length", "Short", "Medium", "Long" },
{ "Volume", "High", "Medium", "Low" },
{ "Temperature", "High", "Medium", "Low" },
{ "Intensity", "High", "Medium", "Low" },
};
static int i = 0;
DefaultMutableTreeNode root, child, chosen;
782
Thinking in Java
img
JTree tree;
DefaultTreeModel model;
public void init() {
Container cp = getContentPane();
root = new DefaultMutableTreeNode("root");
tree = new JTree(root);
// Add it and make it take care of scrolling:
cp.add(new JScrollPane(tree),
BorderLayout.CENTER);
// Capture the tree's model:
model =(DefaultTreeModel)tree.getModel();
JButton test = new JButton("Press me");
test.addActionListener(newActionListener() {
public void actionPerformed(ActionEvent e){
if(i < data.length) {
child = new Branch(data[i++]).node();
// What's the last one you clicked?
chosen = (DefaultMutableTreeNode)
tree.getLastSelectedPathComponent();
if(chosen == null) chosen = root;
// The model will create the
// appropriate event. In response, the
// tree will update itself:
model.insertNodeInto(child, chosen, 0);
// This puts the new node on the
// currently chosen node.
}
}
});
// Change the button's colors:
test.setBackground(Color.blue);
test.setForeground(Color.white);
JPanel p = new JPanel();
p.add(test);
cp.add(p, BorderLayout.SOUTH);
}
public static void main(String[] args) {
Console.run(new Trees(), 250, 250);
}
} ///:~
Chapter13: Creating Windows & Applets
783
img
Thefirst class, Branch, is a tool to take an array of Stringandbuild a
DefaultMutableTreeNodewiththe first Stringas the root andthe
rest of the Strings in the array as leaves. Thennode( ) can be called to
producethe root of this"branch."
TheTreesclasscontains a two-dimensional array of Strings from which
Branches can be made and a static int i to count through thisarray.
TheDefaultMutableTreeNodeobjectshold the nodes, butthe physical
representation on screen is controlled by theJTreeandits associated
model,the DefaultTreeModel. Note that when theJTreeis added to
theapplet, it is wrapped in a JScrollPane--this is all it takes to provide
automaticscrolling.
TheJTreeis controlled through itsmodel. When you make a change to
themodel, the model generates an event that causes theJTreeto
performany necessary updates to thevisible representation of thetree. In
init( ), the model is captured by calling getModel( ). When the button is
pressed, a new "branch" is created.Then the currentlyselected
component is found (or the root is used if nothing is selected)and the
model'sinsertNodeInto( ) methoddoes all the work of changing the
treeand causing it to be updated.
An example like the oneabove may give youwhat you need in a tree.
However,trees have the power to do just about anything youcan
imagine--everywhereyou see the word"default" in the exampleabove,
youcan substitute your ownclass to get differentbehavior. But beware:
almostall of these classes have a large interface, so youcould spend a lot
of time struggling to understandthe intricacies of trees.Despite this, it's a
gooddesign and the alternativesare usually muchworse.
Tables
Liketrees, tables in Swing arevast and powerful. Theyare primarily
intended to be the popular "grid"interface to databases viaJava Database
Connectivity(JDBC, discussed in Chapter15) and thus theyhave a
tremendousamount of flexibility, whichyou pay for in complexity.
There'seasily enough here to be thebasis of a full-blown spreadsheetand
couldprobably justify an entirebook. However, it is alsopossible to create
a relatively simple JTableif you understand thebasics.
784
Thinking in Java
img
TheJTablecontrolshow the data is displayed,but the TableModel
controlsthe data itself. So to create a JTableyou'lltypically create a
TableModelfirst.You can fully implementthe TableModelinterface,
butit's usually simpler to inherit from the helperclass
AbstractTableModel:
//: c13:Table.java
// Simple demonstration of JTable.
// < applet code=Table
//   width=350 height=200></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.table.*;
import javax.swing.event.*;
import com.bruceeckel.swing.*;
public class Table extends JApplet {
JTextArea txt = new JTextArea(4, 20);
// The TableModel controls all the data:
class DataModel extends AbstractTableModel {
Object[][] data = {
{"one", "two", "three", "four"},
{"five", "six", "seven", "eight"},
{"nine", "ten", "eleven", "twelve"},
};
// Prints data when table changes:
class TML implements TableModelListener {
public void tableChanged(TableModelEvent e){
txt.setText(""); // Clear it
for(int i = 0; i < data.length; i++) {
for(int j = 0; j < data[0].length; j++)
txt.append(data[i][j] + " ");
txt.append("\n");
}
}
}
public DataModel() {
addTableModelListener(new TML());
}
public int getColumnCount() {
Chapter13: Creating Windows & Applets
785
img
return data[0].length;
}
public int getRowCount() {
return data.length;
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public void
setValueAt(Object val, int row, int col) {
data[row][col] = val;
// Indicate the change has happened:
fireTableDataChanged();
}
public boolean
isCellEditable(int row, int col) {
return true;
}
}
public void init() {
Container cp = getContentPane();
JTable table = new JTable(new DataModel());
cp.add(new JScrollPane(table));
cp.add(BorderLayout.SOUTH, txt);
}
public static void main(String[] args) {
Console.run(new Table(), 350, 200);
}
} ///:~
DataModel contains an array of data, but youcould also get thedata
fromsome other source such as a database. The constructoradds a
TableModelListenerthatprints the array everytime the table is
changed.The rest of the methodsfollow the Beans namingconvention,
andare used by JTablewhen it wants to present theinformation in
DataModel. AbstractTableModelprovidesdefault methods for
setValueAt( ) andisCellEditable( ) thatprevent changes to thedata,
so if you want to be able to edit the data, youmust override these
methods.
786
Thinking in Java
img
Onceyou have a TableModel, you only need to hand it to the JTable
constructor.All the details of displaying, editing, andupdating will be
takencare of for you. Thisexample also puts theJTablein a
JScrollPane.
Selecting Look & Feel
One of the very interestingaspects of Swing is the"Pluggable Look &
Feel."This allows your program to emulate the look andfeel of various
operatingenvironments. You can even do all sorts of fancy thingslike
dynamicallychanging the look andfeel while the program is executing.
However,you generally just want to do one of two things, eitherselect the
"crossplatform" look and feel(which is Swing's "metal"), or select the
lookand feel for thesystem you are currentlyon, so your Javaprogram
lookslike it was createdspecifically for thatsystem. The code to select
either of these behaviors is quitesimple--but you must execute it before
youcreate any visualcomponents, because thecomponents will be made
based on the current look andfeel and will not be changed just because
youhappen to change the lookand feel midway duringthe program (that
process is more complicated anduncommon, and is relegated to Swing-
specificbooks).
Actually, if you want to use thecross-platform ("metal") lookand feel that
is characteristic of Swing programs,you don't have to do anything--it's
thedefault. But if you wantinstead to use the currentoperating
environment'slook and feel, youjust insert the followingcode, typically at
thebeginning of your main( ) butsomehow before anycomponents are
added:
try {
UIManager.setLookAndFeel(UIManager.
getSystemLookAndFeelClassName());
} catch(Exception e) {}
Youdon't need anything in thecatchclausebecause the UIManager
willdefault to the cross-platformlook and feel if yourattempts to set up
any of the alternatives fail.However, during debuggingthe exception can
be quite useful so you may at least want to put a printstatement in the
catchclause.
Chapter13: Creating Windows & Applets
787
img
Here is a program that takes a command-line argument to select a look
andfeel, and shows howseveral different componentslook under the
chosenlook and feel:
//: c13:LookAndFeel.java
// Selecting different looks & feels.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.bruceeckel.swing.*;
public class LookAndFeel extends JFrame {
String[] choices = {
"eeny", "meeny", "minie", "moe", "toe", "you"
};
Component[] samples = {
new JButton("JButton"),
new JTextField("JTextField"),
new JLabel("JLabel"),
new JCheckBox("JCheckBox"),
new JRadioButton("Radio"),
new JComboBox(choices),
new JList(choices),
};
public LookAndFeel() {
super("Look And Feel");
Container cp = getContentPane();
cp.setLayout(newFlowLayout());
for(int i = 0; i < samples.length; i++)
cp.add(samples[i]);
}
private static void usageError() {
System.out.println(
"Usage:LookAndFeel[cross|system|motif]");
System.exit(1);
}
public static void main(String[] args) {
if(args.length == 0) usageError();
if(args[0].equals("cross")) {
try {
788
Thinking in Java
img
UIManager.setLookAndFeel(UIManager.
getCrossPlatformLookAndFeelClassName());
} catch(Exception e) {
e.printStackTrace(System.err);
}
} else if(args[0].equals("system")) {
try {
UIManager.setLookAndFeel(UIManager.
getSystemLookAndFeelClassName());
} catch(Exception e) {
e.printStackTrace(System.err);
}
} else if(args[0].equals("motif")) {
try {
UIManager.setLookAndFeel("com.sun.java."+
"swing.plaf.motif.MotifLookAndFeel");
} catch(Exception e) {
e.printStackTrace(System.err);
}
} else usageError();
// Note the look & feel must be set before
// any components are created.
Console.run(newLookAndFeel(), 300, 200);
}
} ///:~
Youcan see that oneoption is to explicitly specify a string for a lookand
feel, as seen with MotifLookAndFeel. However, that one andthe
default"metal" look and feelare the only onesthat can legally be used on
anyplatform; even though thereare strings for Windowsand Macintosh
lookand feels, those canonly be used on theirrespective platforms(these
areproduced when you callgetSystemLookAndFeelClassName( )
andyou're on that particularplatform).
It is also possible to create a custom look and feelpackage, for example, if
youare building a framework for a company that wants a distinctive
appearance.This is a big job and is far beyond the scope of this book (in
fact,you'll discover it is beyondthe scope of many dedicatedSwing
books!).
Chapter13: Creating Windows & Applets
789
img
The clipboard
TheJFC supports limitedoperations with the systemclipboard (in the
java.awt.datatransferpackage).You can copy Stringobjects to the
clipboard as text, and you canpaste text from theclipboard into String
objects. Of course, the clipboard is designed to hold any type of data, but
howthis data is represented on the clipboard is up to theprogram doing
thecutting and pasting. TheJava clipboard API providesfor extensibility
throughthe concept of a "flavor."When data comes offthe clipboard, it
has an associated set of flavorsthat it can be converted to (for example, a
graphmight be represented as a string of numbers or as an image)and
youcan see if that particularclipboard data supports theflavor you're
interestedin.
Thefollowing program is a simpledemonstration of cut, copy,and paste
withStringdata in a JTextArea. One thing you'll notice is that the
keyboardsequences you normally usefor cutting, copying, andpasting
alsowork. But if you look at any JTextFieldor JTextArea in any other
programyou'll find that theyalso automatically supportthe clipboard key
sequences.This example simply addsprogrammatic control of the
clipboard,and you could usethese techniques if you want to capture
clipboardtext into something otherthan a JTextComponent.
//: c13:CutAndPaste.java
// Using the clipboard.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import com.bruceeckel.swing.*;
public class CutAndPaste extends JFrame  {
JMenuBar mb = new JMenuBar();
JMenu edit = new JMenu("Edit");
JMenuItem
cut = new JMenuItem("Cut"),
copy = new JMenuItem("Copy"),
paste = new JMenuItem("Paste");
JTextArea text = new JTextArea(20, 20);
Clipboard clipbd =
790
Thinking in Java
img
getToolkit().getSystemClipboard();
public CutAndPaste()  {
cut.addActionListener(new CutL());
copy.addActionListener(new CopyL());
paste.addActionListener(new PasteL());
edit.add(cut);
edit.add(copy);
edit.add(paste);
mb.add(edit);
setJMenuBar(mb);
getContentPane().add(text);
}
class CopyL implements ActionListener {
public void actionPerformed(ActionEvent e) {
String selection = text.getSelectedText();
if (selection == null)
return;
StringSelection clipString =
new StringSelection(selection);
clipbd.setContents(clipString,clipString);
}
}
class CutL implements ActionListener {
public void actionPerformed(ActionEvent e) {
String selection = text.getSelectedText();
if (selection == null)
return;
StringSelection clipString =
new StringSelection(selection);
clipbd.setContents(clipString,clipString);
text.replaceRange("",
text.getSelectionStart(),
text.getSelectionEnd());
}
}
class PasteL implements ActionListener {
public void actionPerformed(ActionEvent e) {
Transferable clipData =
clipbd.getContents(CutAndPaste.this);
try {
String clipString =
Chapter13: Creating Windows & Applets
791
img
(String)clipData.
getTransferData(
DataFlavor.stringFlavor);
text.replaceRange(clipString,
text.getSelectionStart(),
text.getSelectionEnd());
} catch(Exception ex) {
System.err.println("Not String flavor");
}
}
}
public static void main(String[] args) {
Console.run(newCutAndPaste(), 300, 200);
}
} ///:~
Thecreation and addition of themenu and JTextArea should by now
seem a pedestrian activity. What'sdifferent is the creation of the
Clipboardfieldclipbd, which is done through theToolkit.
Allthe action takes place in the listeners. The CopyL andCutL listeners
arethe same except forthe last line of CutL, which erases the linethat's
beencopied. The special twolines are the creation of a StringSelection
objectfrom the Stringandthe call to setContents( ) withthis
StringSelection. That's all there is to putting a Stringon the clipboard.
In PasteL,data is pulled off the clipboardusing getContents( ). What
comesback is a fairly anonymousTransferableobject,and you don't
reallyknow what it contains. Oneway to find out is to call
getTransferDataFlavors( ), which returns an array of DataFlavor
objectsindicating which flavors aresupported by this particularobject.
Youcan also ask it directlywith isDataFlavorSupported( ), passing in
theflavor you're interested in.Here, however, the boldapproach is taken:
getTransferData( ) is called assuming that thecontents supports the
Stringflavor,and if it doesn't theproblem is sorted out in theexception
handler.
In the future you canexpect more data flavors to be supported.
792
Thinking in Java
img
Packaging an applet into a
JAR file
An important use of the JARutility is to optimize appletloading. In Java
1.0,people tended to try to cramall their code into a single applet class so
theclient would need only a single server hit to download the appletcode.
Notonly did this result in messy, hard to read (andmaintain) programs,
butthe .classfilewas still uncompressed so downloading wasn't as fast
as it could have been.
JARfiles solve the problem by compressing all of your.classfilesinto a
singlefile that is downloaded by the browser. Now youcan create the
rightdesign without worryingabout how many .classfiles it will
generate,and the user willget a much faster downloadtime.
ConsiderTicTacToe.java. It looks like a singleclass, but in fact it
containsfive inner classes, so that's six in all. Onceyou've compiled the
program,you package it into a JARfile with theline:
jar cf TicTacToe.jar *.class
Thisassumes that the only.classfiles in the current directory arethe
onesfrom TicTacToe.java(otherwiseyou'll get extrabaggage).
Nowyou can create an HTMLpage with the newarchivetag to indicate
thename of the JAR file.Here is the tag usingthe old form of theHTML
tag, as an illustration:
<head><title>TicTacToeExample Applet
</title></head>
<body>
<applet code=TicTacToe.class
archive=TicTacToe.jar
width=200 height=100>
</applet>
</body>
You'llneed to put it into thenew (messy, complicated)form shown earlier
in the chapter in order to get it to work.
Chapter13: Creating Windows & Applets
793
img
Programming techniques
Because GUI programming in Java hasbeen an evolving technologywith
somevery significant changesbetween Java 1.0/1.1 andthe Swing library
in Java 2, there have beensome old programming idiomsthat have
seepedthrough to examples that youmight see given forSwing. In
addition,Swing allows you to program in more and better waysthan were
allowed by the old models. In thissection, some of theseissues will be
demonstrated by introducing and examiningsome programmingidioms.
Bindingevents dynamically
One of the benefits of the Swingevent model is flexibility.You can add
andremove event behavior withsingle method calls. Thefollowing
exampledemonstrates this:
//: c13:DynamicEvents.java
// You can change event behavior dynamically.
// Also shows multiple actions for an event.
// < applet code=DynamicEvents
//   width=250 height=400></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.bruceeckel.swing.*;
public class DynamicEvents extends JApplet {
ArrayList v = new ArrayList();
int i = 0;
JButton
b1 = new JButton("Button1"),
b2 = new JButton("Button2");
JTextArea txt = new JTextArea();
class B implements ActionListener {
public void actionPerformed(ActionEvent e) {
txt.append("A button was pressed\n");
}
}
class CountListener implements ActionListener {
794
Thinking in Java
img
int index;
public CountListener(int i) { index = i; }
public void actionPerformed(ActionEvent e) {
txt.append("Counted Listener "+index+"\n");
}
}
class B1 implements ActionListener {
public void actionPerformed(ActionEvent e) {
txt.append("Button 1 pressed\n");
ActionListener a = new CountListener(i++);
v.add(a);
b2.addActionListener(a);
}
}
class B2 implements ActionListener {
public void actionPerformed(ActionEvent e) {
txt.append("Button2pressed\n");
int end = v.size() - 1;
if(end > = 0) {
b2.removeActionListener(
(ActionListener)v.get(end));
v.remove(end);
}
}
}
public void init() {
Container cp = getContentPane();
b1.addActionListener(new B());
b1.addActionListener(new B1());
b2.addActionListener(new B());
b2.addActionListener(new B2());
JPanel p = new JPanel();
p.add(b1);
p.add(b2);
cp.add(BorderLayout.NORTH, p);
cp.add(new JScrollPane(txt));
}
public static void main(String[] args) {
Console.run(newDynamicEvents(), 250, 400);
}
} ///:~
Chapter13: Creating Windows & Applets
795
img
Thenew twists in this exampleare:
1.
There is more than one listenerattached to each Button. Usually,
componentshandle events as multicast, meaning that youcan
registermany listeners for a singleevent. In the special
components in which an event is handled as unicast, you'll get a
TooManyListenersException.
2.
Duringthe execution of theprogram, listeners aredynamically
addedand removed from theButton b2. Adding is accomplished
in the way you've seenbefore, but each componentalso has a
removeXXXListener( ) method to remove each type of listener.
Thiskind of flexibility providesmuch greater power in your
programming.
Youshould notice that eventlisteners are not guaranteed to be called in
theorder they are added(although most implementations do in fact work
thatway).
Separatingbusiness logic
from UI logic
In general you'll want to design your classes so thateach one does"only
onething." This is particularlyimportant when user-interfacecode is
concerned,since it's easy to tie up "what you're doing" with"how you're
displayingit." This kind of couplingprevents code reuse. It'smuch more
desirable to separate your "businesslogic" from the GUI.This way, you
cannot only reuse thebusiness logic more easily,it's also easier to reuse
theGUI.
Anotherissue is multitieredsystems,where the "business objects"reside
on a completely separate machine.This central location of thebusiness
rulesallows changes to be instantlyeffective for all newtransactions, and
is thus a compelling way to set up a system. However, thesebusiness
objectscan be used in manydifferent applications and so should not be
tied to any particular mode of display. They should justperform the
businessoperations and nothingmore.
796
Thinking in Java
img
Thefollowing example shows howeasy it is to separate thebusiness logic
fromthe GUI code:
//: c13:Separation.java
// Separating GUI logic and business objects.
// < applet code=Separation
// width=250 height=150> </applet>
import javax.swing.*;
import java.awt.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.applet.*;
import com.bruceeckel.swing.*;
class BusinessLogic {
private int modifier;
public BusinessLogic(int mod) {
modifier = mod;
}
public void setModifier(int mod) {
modifier = mod;
}
public int getModifier() {
return modifier;
}
// Some business operations:
public int calculation1(int arg) {
return arg * modifier;
}
public int calculation2(int arg) {
return arg + modifier;
}
}
public class Separation extends JApplet {
JTextField
t = new JTextField(15),
mod = new JTextField(15);
BusinessLogic bl = new BusinessLogic(2);
JButton
calc1 = new JButton("Calculation 1"),
Chapter13: Creating Windows & Applets
797
Table of Contents:
  1. Introduction to Objects:The progress of abstraction, An object has an interface
  2. Everything is an Object:You manipulate objects with references, Your first Java program
  3. Controlling Program Flow:Using Java operators, Execution control, true and false
  4. Initialization & Cleanup:Method overloading, Member initialization
  5. Hiding the Implementation:the library unit, Java access specifiers, Interface and implementation
  6. Reusing Classes:Composition syntax, Combining composition and inheritance
  7. Polymorphism:Upcasting revisited, The twist, Designing with inheritance
  8. Interfaces & Inner Classes:Extending an interface with inheritance, Inner class identifiers
  9. Holding Your Objects:Container disadvantage, List functionality, Map functionality
  10. Error Handling with Exceptions:Basic exceptions, Catching an exception
  11. The Java I/O System:The File class, Compression, Object serialization, Tokenizing input
  12. Run-time Type Identification:The need for RTTI, A class method extractor
  13. Creating Windows & Applets:Applet restrictions, Running applets from the command line
  14. Multiple Threads:Responsive user interfaces, Sharing limited resources, Runnable revisited
  15. Distributed Computing:Network programming, Servlets, CORBA, Enterprise JavaBeans
  16. A: Passing & Returning Objects:Aliasing, Making local copies, Cloning objects
  17. B: The Java Native Interface (JNI):Calling a native method, the JNIEnv argument
  18. Java Programming Guidelines:Design, Implementation
  19. Resources:Software, Books, My own list of books
  20. Index