Introduction to the AWT

Copyright 1995 by Nelson Yu

First sips: An Overview of the AWT

AWT stands for Abstract Window ToolKit. It is a portable GUI library between Solaris and Windows 95/NT and Mac System 7.X(soon) for stand-alone applications and/or applets. Since it can be used in applets it can be used on IRIX, SunOS, HP/UX, Linux which Netscape 2.0 supports.

The Abstract Window Toolkit provides many classes for programmers to use. It is your connection between your application and the native GUI. The AWT hides you from the underlying details of the GUI your application will be running on and thus is at very high level of abstraction. It takes the lowest common denominator approach to retain portability. No floating toolbars or Balloon help here...

It is a Java package and can be used in any Java program by importing java.awt.* via the import keyword. The documentation for the package is available at the Java hompage. The package will be covered briefly as this document is not considered advanced material because it does not discuss Peers, ImageConsumers/Producers, Toolkits and other advanced AWT ilk. It is recommend you look at the source code to see how the AWT really works.

Java concepts

But first lets cover some basics that you'll need to know before diving into the AWT package. I briefly mention some fundamental Java concepts and if they ring a bell then you should continue, otherwise, you should get a better grasp of Java first.

Classes: public, protected and private

Almost every thing is a class in Java, except for primitives such as ints, chars, doubles, etc. And all classes(reference types according to the lang spec) are derived from Object.

The cornerstone of most OO programing languages are classes. So I'd expect you to understand them before using the AWT.

Java has four types of access levels and one unamed default one:

The default access level is very similar to protected.

this and super

this refers to the current instance of class, where as super refers to the parent of the class.
public class MyClass extends Object
{
        int x;
        int y;
        int z;
        
        ...
        public void myMethod(int x, int y, int size)
        {
                this.x = x;
                this.y = y 
                z = size;  
                
                // Error: Ambiguous
                x = x;
                y = y 
        }
         
}        
         
public class YourClass extends MyClass
{
        int x;
        int y;
        int size;
        
        ...
        public void yourMethod(int x, int y)
        {
                // This sets MyClass' x, y,z variables
                super.x = x;
                super.y = y;
                super.z = x + y;
                
                this.x = x;
                this.y = y;
                
                size = x * y * super.x * super.y;
        }
}

Class methods and variables vs. Instance methods and variables

A static member belongs to the class and not to the instance, thus it is illegal to access a static member from a non-static method without qualifying it i.e
public class AnotherClass
{
        static int x;
        int y;
        
        public void myMethod()
        {
                y = AnotherClass.x;
        }
}
If you have a C++ background this is rather straightforward. An instance method/variable can be access using the "this" pointer or not. In the above code y and this.y are exactly the same.

With class methods and variables you do not need to create an instance of the class in order to use the method/variables. For example, System.out.println() is accessible without creating an instance of System because out is a static PrintStream.

Inheritance and Interfaces:

With class inheritance you inherit the interface and implementation. Whereas with interfaces you just inherit the interface. In Java, only single inheritance with classes is possible, but interfaces can be multiply inherited.

interface Document
{
	public void read();
	public void write();
}

public class MultipleInterfaces extends Object implements Runnable, Document
{
	// Runnable requires this
	public void run() {}

	// Document require these
	public void read() {}
	public void write() {}
}

When you inherit from a class

You inherit the public members, have access to protected members of the parent, but not to private member of the parent. The new 'derived'(child by others) is considered a sub-class or sub-type of the class you derived from.

When you implement an interface

You must implement the methods specified by the interface otherwise the class becomes abstract. See above.

Polymorphism and Object

Since every class is an Object(it's the ultimate parent of all classes) we can do this..
public class ThisClass
{
	public myMethod(Object x, Object y)
	{
		if(x instanceof Button && y instanceof Button)
			System.out.println("This method was called with Buttons");
		else if(x instanceof Thread && y instanceof Thread)
			System.out.println("This method was callled with Threads");
		else if(x instanceof Label && y instanceof String)
		{
			Label l = (Label)x;
			l.setLabel((String)y);
		}
	}
}
Comfortable with what has been discussed? If yes, you should read this and the tutorial. If not you could read this for a brief overview of AWT programming.

Structure of the AWT

The structure of the AWT is rather simple: Components are added to and then laid out by layoutmanagers in Containers. We have a variety of event handling, menu, fonts, graphics classes in addition to those two, but they'll be only briefly discussed here.

Nothing prevents us from using threads, audio, I/O, networking classes alongside the AWT. The AWT by itself is rather bare and empty. Imagine a text editor that couldn't save! As a side note, the AWT can be used in a stand-alone Java application or in an applet that is embedded within a HTML document.

The AWT hierarchy

Classes:

Interfaces


Components and Containers:

There are two user-interface classes in the AWT to focus on: Components and Containers.

Containers

Containers(Frames, Dialogs, Windows and Panels) can contain components and are themselves components, thus can be added to Containers. Containers usually handle events that occurred to the Components, although nothing prevents you from handling events in the component. In short all classes derived from Container can be one.

The method of handling events in the Container(i.e Frame) is preferred over the latter, since we want to centralize event handling. If you don't want to handle events in one common area of code, then you must sub-class every Component you create an instance of and override its action() or handleEvent() method.

public class TextEditor extends Frame
{
	Button myButton;

	public TextEditor()
	{
	...
		setTitle("Text Editor")
		setLayout(new BorderLayout());
		setBackground(Color.orange);

		myButton = new Button("Hit This");
		add("Center", myButton);
		
		pack();
		show();
	}

	public boolean handleEvent(Event evt)
	{
		if("Hit This".equals(evt.arg))
		{
			System.out.println("He hit me");
			return true;
		}
	}

}
This is another method of event handling. The event is handled in the Component rather than the Container. First we must sub-class Button.
public class MyButton extends Button
{
	...
	...
	public boolean action(Event evt, Object arg)
	{
		if(evt.target == this)
		{
			System.out.println("He hit me");
			return true;
		}		
	}
}
public class AnotherTextEditor extends Frame
{
	MyButton anotherButton;

	public AnotherTextEditor()
	{
		setTitle("Another Text Editor")
		setLayout(new BorderLayout());
		setBackground(Color.white);

		anotherButton = new MyButton("Hit This");
		add("Center", anotherButton);
		
		pack();
		resize(300, 300);
		show();
	}
}
All Containers have common functionality, due to the fact they are derived from Container which includes many pre-defined event handling methods(called callbacks). These events are useful for handling user input in a specialized(i.e sub-class or derived) Container class where you'd override the default behavior such as the appearance(i.e font, color, etc.) by overriding the methods.

A quick-and-dirty summary of common methods:

add(Component)
add(String, Component)
remove(Component)
getComponents()
setLayout(LayoutManager)
getLayout()

Every Container Is-A Component since it is derived from Component, thus it can also behave and be used like an Component. All of the methods in Component can be used in a Container.

Components

Components are generally the stuff that the user interacts with. The meat and potatoes of your application. You'll need to display Windows and Buttons. You'll want Lists within Windows. You'll want the user to enter some text/input. You'll want easy access to features and functions. Components will do that for you.

Components are Buttons, TextAreas, Scrollbars, etc. in other words the visible UI controls that the user interacts with, all of which have been added to a Container. Anything that is derived from the class Component can be one. p

A quick-and-dirty summary of common methods:

getBackground()
setBackground(Color)
getFont()
setFont(Font)
mouseDown(Event, int, int)
show()
resize(int, int)
paint(Graphics)
update(Graphics)
move(int, int)


Layouts:

How Components are "laid out" within a Container is described by the LayoutManager class. Since the LayoutManager class is abstract, we can not use it directly. You must sub-class it and provide your own functionality or use a derived class of LayoutManager(i.e BorderLayout, CardLayout, GridLayout, etc) already created for you.

There are man different layout schemes, but the ones pre-defined for us are

To use a layout we must call setLayout() for the Container with an instance of a LayoutManager(since LayoutManager is abstract, a sub-class will do).

Using each layouts is different. For BorderLayout you specify the type of layout(North, South, etc.) by passing a string(i.e "North", "South", etc to the method add() in a Container.

i.e

add("Center", myComponent);
adds myComponent to the Container and centers it. With other layouts, the first String parameter is different.


Using Components and Containers

Like all Java classes, to use a class from the AWT we need to create an instance of it via "new".
add(new Button("Hello"));
Button b = new Button("World");
add(b);


Event handling

Event-handling? Geez don't I just poll and grab user-input?

No, this is Java and how the GUIs it runs under operate. GUIs and Windowing systems use the event-driven model rather than the old procedural-grab-the-input-yourself model. The OS is the one polling for events. When it finds ones, it tells you what has happened. If you say, "bah, I don't care about that event". Fine, the OS handles it with its own default handler, otherwise you better do something with it or you get the default, which maybe nothing at all.

Here are the some of the events you will need to handle. This list of all events your application will probably need to handle is in the Event's class documentation.

  1. ACTION_EVENT
  2. MOUSE_DOWN
  3. KEY_PRESS
  4. WINDOW_DESTROY

ACTION_EVENT occurs when you press a button, select a menu, etc.
To handle ACTION_EVENT you have two choices

  1. Handle it handleEvent()
  2. Handle it in action()
Both are located in the Component class so all derived classes have these methods and you should override them to provide your own custom behavior.

The following examples shows you how and why...

public boolean handleEvent(Event evt)
{                
	switch(evt.id)
        {
        	case Event.ACTION_EVENT:
                {
			if((Hey I've been hit " + n + "times).equals(evt.arg))
			{
				return true;
			}
			// Here evt.target contains the target
			// i.e java.awt.MenuItem[label=Quit]
			// evt.arg contains the label of the button pressed
			// or menu item hit
                }
		default:
			return false;
	}
}
The same thing came be accomplished with action(evt, arg), because when an ACTION_EVENT occurs action() is called.
public boolean action(Event evt, Object arg)
{
		if(("Hey I've been hit " + n + "times").equals(arg))
		{
			return true;
		}
	return false;
}
As you can see, even if the labels change you can still receive the event. The above is a button that changes everytime you hit it. But this isn't the only way to handle events. In fact you can compare objects instead of labels such as if(evt.target == myButton).
public boolean handleEvent(Event evt)
{
        switch(evt.id)
        {
		// Examples of what can be handled
                case Event.KEY_PRESS:
		case Event.HOME:
		case Event.WINDOW_MOVED:
			return true;
		// ...

		default:
			return false;
        }

}
Also you can override a sub-class's method handler such as keyDown(), mouseDown() to provide your own event handling code. For example a specialized(sub-class) version of a TextArea that overrides mouseEnter() can determine when the mouse enters the Component and thus can decide to hilight the text if needed.


To view this applet you'll need a Java-enabled browser

The source code


HelloWorld: Here it is again

The infamous and canonical example will be shown here to demostrate simple features of the AWT. This example does not use the AWT at all and displays the string "Hello World" to the standard output. You ask "Why doesn't it display to a Window"?
import java.*;

public class MyHelloWorld
{
	public static void main(String args[])
	{
   	         System.out.println("Hello World");
	}
}
Have we created a Window? have we specified the font size, color and placement of the string "Hello World"? No. GUIs were never meant for printing "Hello World" onto the screen graphically, but to make applications easier-to-use.

This example displays the string graphically and shows you the types of errors you can get into if you are not careful.

Notice how the first string is cut-off:


To view this applet you'll need a Java-enabled browser

The source code

import java.awt.*;

public class GraphicHello extends Frame
{
        // Constants
	static final int H_SIZE = 300;
	static final int V_SIZE = 200;

        // The constructor
	public GraphicHello()
	{
		setFont(new Font("TimesRoman",Font.BOLD,24));
		setBackground(Color.white);
                setTitle("GraphicHello");
		resize(H_SIZE,V_SIZE);
		show();
	}

	public void paint(Graphics g)
	{
		// This gets cut-off because the baseline is
		// too near to the top.
		// We also want to change the color of the font
                // when we draw
		g.setColor(Color.black);
		g.drawString("Hello World", 10,10);

		// This is just fine for this window
		g.setColor(Color.red);
		g.drawString("Hello", 10,50);

	}

	public static void main(String args[])
	{
		new GraphicHello();
	}
}

Menus

Menus are at the center of your application. The difference between a usuable application and one that is absolutely frustrating lies in the organization of your menus. The user-interfaces rules for a good menu design are not clear.

The following classes are related to Menus

Handling is done in the handleEvent() method which you will override.

To use menus you need to create a MenuBar and Menus with MenuItems, then attach the MenuBar to your application via setMenuBar. You can even embed, Menus in MenuItems..


MenuBar mb = new MenuBar();
Menu  top = new Menu("File");

Menu  sub = new Menu("New");
sub.add(new MenuItem("Document"));
sub.add(new MenuItem("Message"));
sub.add(new MenuItem("Image"));
top.add(sub);

top.add(new MenuItem("Open"));
top.add(new MenuItem("Save..));

setMenuBar(mb);
To use Menus in applet you'll need to create a new Frame and attach a menubar to that. You can not just change the browser's menubar. You are not dealing with OLE 2 here.


Simple Graphics

Drawing Lines:

If you want to draw a line this is the method you would use
 
g.drawLine(x1, y1, width, height);
Where g is an instance of the Graphics class. graphics.drawLine(...) is also legal as long as 'graphics' is an instance of Graphics.

Note: Graphics is an abstract class, you cannot just 'new' it.
i.e

g = new Graphics() is illegal.
Here is some code that demostrates drawLine. The example was converted from C/C++ from a book I can't remember the title of.
 
import java.awt.*;

public class FiftySquares extends Frame
{
	float xA, yA, xB, yB, xC, yC, xD, yD,
			xA1, yA1, xB1, yB1, xC1, yC1, xD1, yD1,p,q,r;
	int i;
	int x_center, y_center;

	static final int H_SIZE = 300;
	static final int V_SIZE = 300;

	public FiftySquares()
	{
		q = (float)0.05;
		p = 1 - q;
		r = (float)0.95 * H_SIZE;

		x_center = H_SIZE/2;
		y_center = V_SIZE/2;

		resize(H_SIZE, V_SIZE);
		show();
	}

	public void paint(Graphics g)
	{
		xA = xD = x_center - r;
		xB = xC = x_center + r;
		yA = yB = y_center - r;
		yC = yD = y_center + r;

		for(i=0; i<50; i++)
		{
			g.drawLine((int)xA, (int)yA, (int)xB, (int)yB);
			g.drawLine((int)xB, (int)yB, (int)xC, (int)yC);
			g.drawLine((int)xC, (int)yC, (int)xD, (int)yD);
			g.drawLine((int)xD, (int)yD, (int)xA, (int)yA);
			xA1= p*xA+q*xB;yA1= p*yA+q*yB;
			xB1= p*xB+q*xC;yB1= p*yB+q*yC;
			xC1= p*xC+q*xD;yC1= p*yC+q*yD;
			xD1= p*xD+q*xA;yD1= p*yD+q*yA;
			xA=xA1;xB=xB1;xC=xC1;xD=xD1;
			yA=yA1;yB=yB1;yC=yC1;yD=yD1;
		}
	}

	public boolean mouseDown(Event evt, int x, int y)
	{
		repaint();
		return true;
	}

	public static void main(String args[])
	{
			new FiftySquares();
	}
}


To view this applet you'll need a Java-enabled browser

The source code

Drawing Rectangles:

To draw an rectangle you would use one of the following
drawRect(x1, y1, width, height)
drawRoundRect(x1, y1, width, height, arcWidth, arcHeight)
drawRect3D(x1, y1, width, height, raised)

Drawing images

Drawing an image is done with drawImage a method in the Graphics class. But first you drawImage(Image, x, y, ImageObserver) drawImage(Image, x1, y1, x2, y2, ImageObserver) An example in usage. Any class that implements the ImageObserver interface and has the ability to retrieve an image is OK with the following code. You're in luck. All Components implement the ImageObserver interface and applets can use getImage which retrives a gif/jpeg and returns an Image class

Image img = getImage(getDocumentBase(), "mypicture.gif");

..

public void paint(Graphics g)
{
	g.drawImage(img, 0, 0, this);
}

Note: In a stand-alone application. You can grab an image by calling getDefaultTookit().getImage(...) But there is a bug in the SecurityManager in the JDK beta 2 that prevent this from working properly.

End of Introduction.


[Next] [Home]
Nelson Yu
nelson@cs.ualberta.ca
Last modified: Jan 2 1996