Internet Programming with Java Course

2.1 Създаване на user interface с Java. Въведение в AWT.

2.2 Създаване на Java аплети – основни концепции

Applets

Hello World: The Applet

The reason people are excited about Java as more than just another OOP language is because it allows them to write interactive applets on the web. Hello World isn't a very interactive program, but let's look at a webbed version.

import java.applet.Applet;  

import java.awt.Graphics;

             

public class HelloWorldApplet extends Applet {

 

  public void paint(Graphics g) {

    g.drawString("Hello world!", 50, 25);

  }

 

}

The applet version of HelloWorld is a little more complicated than the HelloWorld application, and it will take a little more effort to run it as well.

First type in the source code and save it into file called HelloWorldApplet.java. Compile this file in the usual way. If all is well a file called HelloWorldApplet.class will be created. Now you need to create an HTML file that will include your applet. The following simple HTML file will do.

<HTML>
<HEAD>
<TITLE> Hello World </TITLE>
</HEAD>
 
<BODY>
        This is the applet:<P>
        <applet code="HelloWorldApplet.class" width="150" height="50">
        </applet>
</BODY>
</HTML>

Save this file as HelloWorldApplet.html in the same directory as the HelloWorldApplet.class file. When you've done that, load the HTML file into a Java enabled browser like Internet Explorer 4.0 or Sun's applet viewer included with the JDK. You should see something like below, though of course the exact details depend on which browser you use.

If you're using the JDK 1.1 to compile your program, you should use the applet viewer, HotJava, Internet Explorer 4.0 or later, or Netscape 4.0.6 or later on Windows and Unix to view the applet. Netscape Navigator 4.0.5 and earlier and 3.x versions of Internet Explorer do not support Java 1.1. Furthermore, no Mac version of Navigator supports Java 1.1.

If the applet compiled without error and produced a HelloWorldApplet.class file, and yet you don't see the string "Hello World" in your browser chances are that the .class file is in the wrong place. Make sure HelloWorldApplet.class is in the same directory as HelloWorld.html. Also make sure that you're using a version of Netscape or Internet Explorer which supports Java. Not all versions do.

In any case Netscape's Java support is less than the perfect so if you have trouble with an applet, the first thing to try is loading it into Sun's Applet Viewer instead. If the Applet Viewer has a problem, then chances are pretty good the problem is with the applet and not with the browser.

What is an Applet?

According to Sun "An applet is a small program that is intended not to be run on its own, but rather to be embedded inside another application....The Applet class provides a standard interface between applets and their environment."

Four definitions of applet:

 

public class Applet extends Panel

 
java.lang.Object
   |
   +----java.awt.Component
           |
           +----java.awt.Container
                   |
                   +----java.awt.Panel
                           |
                           +----java.applet.Applet

The APPLET HTML Tag

Applets are embedded in web pages using the <APPLET> and </APPLET> tags. The <APPLET> tag is similar to the <IMG> tag. Like <IMG> <APPLET> references a source file that is not part of the HTML page on which it is embedded. IMG elements do this with the SRC attribute. APPLET elements do this with the CODE attribute. The CODE attribute tells the browser where to look for the compiled .class file. It is relative to the location of the source document. Thus if you're browsing http://metalab.unc.edu/javafaq/index.html and that page references an applet with CODE="Animation.class", then the file Animation.class should be at the URL http://metalab.unc.edu/javafaq/animation.class.

For reasons that remain a mystery to HTML authors everywhere if the applet resides somewhere other than the same directory as the page it lives on, you don't just give a URL to its location. Rather you point at the CODEBASE. The CODEBASE attribute is a URL that points at the directory where the .class file is. The CODE attribute is the name of the .class file itself. For instance if on the HTML page of the previous section you had written

<APPLET

        CODE="HelloWorldApplet.class" 
        CODEBASE="classes" 
        WIDTH=200 HEIGHT=200>
</APPLET>
 

then the browser would have tried to find HelloWorldApplet.class in the classes directory in the same directory as the HTML page that included the applet. On the other hand if you had written

 

<APPLET 
        CODE="HelloWorldApplet.class" 
        CODEBASE="http://www.foo.bar.com/classes" 
        WIDTH=200 
        HEIGHT=200>
</APPLET>

 

then the browser would try to retrieve the applet from http://www.foo.bar.com/classes/HelloWorldApplet.class regardless of where the HTML page was.

In short the applet viewer will try to retrieve the applet from the URL given by the formula (CODEBASE + "/" + code). Once this URL is formed all the usual rules about relative and absolute URLs apply.

You can leave off the .class extension and just use the class name in the CODE attribute. For example,

<APPLET CODE="HelloWorldApplet" 
        CODEBASE="http://www.foo.bar.com/classes" 
        WIDTH=200 
        HEIGHT=200>
</APPLET>

 

If the applet is in a non-default package, then the full package qualified name must be used. For example,

 
<APPLET 
        CODE="com.macfaq.greeting.HelloWorldApplet" 
        CODEBASE="http://www.foo.bar.com/classes" 
        WIDTH=200 
        HEIGHT=200>
</APPLET>
 

In this case the browser will look for http://www.foo.bar.com/classes/com/macfaq/greeting/HelloWorldApplet.class so the directory structure on the server should also mirror the package hierarchy.

The HEIGHT and WIDTH attributes work exactly as they do with IMG, specifying how big a rectangle the browser should set aside for the applet. These numbers are specified in pixels and are required.

Spacing Preferences

The <APPLET> tag has several attributes to define how it is positioned on the page.

The ALIGN attribute defines how the applet's rectangle is placed on the page relative to other elements. Possible values include LEFT, RIGHT, TOP, TEXTTOP, MIDDLE, ABSMIDDLE, BASELINE, BOTTOM and ABSBOTTOM. This attribute is optional.

You can specify an HSPACE and a VSPACE in pixels to set the amount of blank space between an applet and the surrounding text. The HSPACE and VSPACE attributes are optional.

<APPLET 
        code="HelloWorldApplet.class" 
        CODEBASE="http://www.foo.bar.com/classes" 
        width=200 
        height=200
        ALIGN=RIGHT 
        HSPACE=5 
        VSPACE=10>
</APPLET>

The ALIGN, HSPACE, and VSPACE attributes are identical to the attributes of the same name used by the <IMG> tag.

Alternate Text

The <APPLET> has an ALT attribute. An ALT attribute is used by a browser that understands the APPLET tag but for some reason cannot play the applet. For instance, if you've turned off Java in Netscape Navigator 3.0, then the browser should display the ALT text. Note that I said it should, not that it does. The ALT tag is optional.

 
<applet code="HelloWorldApplet.class" 
        CODEBASE="http://www.foo.bar.com/classes" width=200 height=200
        ALIGN=RIGHT HSPACE=5 VSPACE=10
        ALT="Hello World!">
</APPLET>

 

ALT is not used by browsers that do not understand <APPLET> at all. For that purpose <APPLET> has been defined to require a closing tag, </APPLET>. All raw text between the opening and closing <APPLET> tags is ignored by a Java capable browser. However a non-Java capable browser will ignore the <APPLET> tags instead and read the text between them. For example the following HTML fragment says Hello to people both with and without Java capable browsers.

 
<APPLET code="HelloWorldApplet.class" 
CODEBASE="http://www.foo.bar.com/classes" 
width=200 
height=200
        ALIGN=RIGHT HSPACE=5 VSPACE=10
        ALT="Hello World!">
        Hello World!<P>
</APPLET>

Naming Applets

You can give an applet a name by using the NAME attribute of the APPLET tag. This allows communication between different applets on the same Web page.

 

<APPLET 
        code="HelloWorldApplet.class" 
        Name=Applet1
        CODEBASE="http://www.foo.bar.com/classes" 
        width=200 
        height=200
        ALIGN=RIGHT HSPACE=5 VSPACE=10
        ALT="Hello World!">
        Hello World!<P>
</APPLET>

JAR Archives

HTTP 1.0 uses a separate connection for each request. When you're downloading many small files, the time required to set up and tear down the connections can be a significant fraction of the total amount of time needed to load a page. It would be better if you could load all the HTML documents, images, applets, and sounds a page needed in one connection.

One way to do this without changing the HTTP protocol, is to pack all those different files into a single archive file, perhaps a zip archive, and just download that.

We aren't quite there yet. Browsers do not yet understand archive files, but in Java 1.1 applets do. You can pack all the images, sounds, and .class files an applet needs into one JAR archive and load that instead of the individual files. Applet classes do not have to be loaded directly. They can also be stored in JAR archives. To do this you use the ARCHIVES attribute of the APPLET tag

<APPLET 
        CODE=HelloWorldApplet 
        WIDTH=200 HEIGHT=100 
        ARCHIVES="HelloWorld.jar">
        <hr>
               Hello World!
        <hr>
</APPLET>

In this example, the applet class is still HelloWorldApplet. However, there is no HelloWorldApplet.class file to be downloaded. Instead the class is stored inside the archive file HelloWorld.jar.

Sun provides a tool for creating JAR archives with its JDK 1.1. For example,

% jar cf HelloWorld.jar *.class

This puts all the .class files in the current directory in the file named "HelloWorld.jar". The syntax of the jar command is deliberately similar to the Unix tar command.

The OBJECT Tag

HTML 4.0 deprecates the <APPLET> tag. Instead you are supposed to use the <OBJECT> tag. For the purposes of ewbedding applets, the <OBJECT> tag is used almost exactly like the <APPLET> tag except that the class attribute becomes the classid attribute. For example,

 
<OBJECT classid="MyApplet.class" 
        CODEBASE="http://www.foo.bar.com/classes" width=200 height=200
        ALIGN=RIGHT HSPACE=5 VSPACE=10>
</OBJECT>
 

The <OBJECT> tag is also used to embed ActiveX controls and other kinds of active content, and it has a few additional attributes to allow it to do that. However, for the purposes of Java you don't need to know about these.

The <OBJECT> tag is supported by Netscape 4.0 and later and Internet Explorer 4.0 and later. It is not supported by earlier versions of those browsers so <APPLET> is unlikely to disappear anytime soon.

You can support both by placing an <APPLET> element inside an <OBJECT> element like this:

<OBJECT classid="MyApplet.class" width=200 height=200>
        <APPLET code="MyApplet.class" width=200 height=200>
        </APPLET>
</OBJECT>
 

Browsers that understand <OBJECT> will ignore its content while browsers that don't will display its content.

PARAM elements are the same for <OBJECT> as for <APPLET>.

Finding an Applet's Size

When running inside a web browser the size of an applet is set by the height and width attributes and cannot be changed by the applet. Many applets need to know their own size. After all you don't want to draw outside the lines. :-)

Retrieving the applet size is straightforward with the getSize() method. java.applet.Applet inherits this method from java.awt.Component. getSize() returns a java.awt.Dimension object. A Dimension object has two public int fields, height and width. Below is a simple applet that prints its own dimensions.

import java.applet.*;   

import java.awt.*;

 

public class SizeApplet extends Applet {

 

  public void paint(Graphics g) {

    Dimension appletSize = this.getSize();

    int appletHeight = appletSize.height;

    int appletWidth = appletSize.width;

   

    g.drawString("This applet is " + appletHeight +

      " pixels high by " + appletWidth + " pixels wide.",

      15, appletHeight/2);

  }

 

}

Note how the applet's height is used to decide where to draw the text. You'll often want to use the applet's dimensions to determine how to place objects on the page. The applet's width wasn't used because it made more sense to left justify the text rather than center it. In other programs you'll have occasion to use the applet width too.

Passing Parameters to Applets

Parameters are passed to applets in NAME=VALUE pairs in <PARAM> tags between the opening and closing APPLET tags. Inside the applet, you read the values passed through the PARAM tags with the getParameter() method of the java.applet.Applet class.

The program below demonstrates this with a generic string drawing applet. The applet parameter "Message" is the string to be drawn.

import java.applet.*;   

import java.awt.*;

            

public class DrawStringApplet extends Applet {

 

  private String defaultMessage = "Hello!";

 

  public void paint(Graphics g) {

    String inputFromPage = this.getParameter("Message");

    if (inputFromPage == null) inputFromPage = defaultMessage;

    g.drawString(inputFromPage, 50, 25);

  }

 

}

You also need an HTML file that references your applet. The following simple HTML file will do:

<HTML>
<HEAD>
<TITLE> Draw String </TITLE>
</HEAD>
 
<BODY>
This is the applet:<P>
<APPLET code="DrawStringApplet.class" width="300" height="50">
         <PARAM name="Message" value="Howdy, there!">
         This page will be very boring if your 
         browser doesn't understand Java.
</APPLET>
</BODY>
</HTML> 

Of course you are free to change "Howdy, there!" to a "message" of your choice. You only need to change the HTML, not the Java source code. PARAMs let you customize applets without changing or recompiling the code.

This applet is very similar to the HelloWorldApplet. However rather than hardcoding the message to be printed it's read into the variable inputFromPage from a PARAM in the HTML.

You pass getParameter() a string that names the parameter you want. This string should match the name of a <PARAM> tag in the HTML page. getParameter() returns the value of the parameter. All values are passed as strings. If you want to get another type like an integer, then you'll need to pass it as a string and convert it to the type you really want.

The <PARAM> HTML tag is also straightforward. It occurs between <APPLET> and </APPLET>. It has two attributes of its own, NAME and VALUE. NAME identifies which PARAM this is. VALUE is the value of the PARAM as a String. Both should be enclosed in double quote marks if they contain white space.

An applet is not limited to one PARAM. You can pass as many named PARAMs to an applet as you like. An applet does not necessarily need to use all the PARAMs that are in the HTML. Additional PARAMs can be safely ignored.

Processing An Unknown Number Of Parameters

Most of the time you have a fairly good idea of what parameters will and won't be passed to your applet. However some of the time there will be an undetermined number of parameters. For instance Sun's imagemap applet passes each "hot button" as a parameter. Different imagemaps have different numbers of hot buttons. Another applet might want to pass a series of URL's to different sounds to be played in sequence. Each URL could be passed as a separate parameter.

Or perhaps you want to write an applet that displays several lines of text. While it would be possible to cram all this information into one long string, that's not too friendly to authors who want to use your applet on their pages. It's much more sensible to give each line its own <PARAM> tag. If this is the case, you should name the tags via some predictable and numeric scheme. For instance in the text example the following set of <PARAM> tags would be sensible:

<PARAM name="Line1" value="There once was a man from Japan">
<PARAM name="Line2" value="Whose poetry never would scan">
<PARAM name="Line3" value="When asked reasons why,">
<PARAM name="Line4" value="He replied, with a sigh:">
<PARAM name="Line5" value="I always try to get as many 
        syllables into the last line as I can.">
 

The program below displays this limerick. Lines are accumulated into an array of strings called poem. A for loop fills the array with the different lines of poetry. There are 101 spaces in the array, but since you won't normally need that many, an if clause tests to see whether the attempt to get a parameter was successful by checking to see if the line is null. As soon as one fails, the loop is broken. Once the loop is finished num_lines is decremented by one because the last line the loop tried to read wasn't there.

The paint() method loops through the poem array and prints each String on the screen, incrementing the y position by fifteen pixels each step so you don't draw one line on top of the other.

Processing An Unknown Number Of Parameters

 

import java.applet.*;   

import java.awt.*;

            

public class PoetryApplet extends Applet

{

 

  private String[] poem = new String[101];

  private int numLines;

 

  public void init() {

    String nextline;

    for (numLines = 1; numLines < poem.length; numLines++) {

      nextline = this.getParameter("Line" + numLines);

      if (nextline == null) break;

      poem[numLines] = nextline;

    }

    numLines--;

  }

 

  public void paint(Graphics g) {

    int y = 15;

    for (int i=1; i <= numLines; i++) {

      g.drawString(poem[i], 5, y);   

      y += 15;

    }

  }

 

}

You might think it would be useful to be able to process an arbitrary list of parameters without knowing their names in advance, if nothing else so you could return an error message to the page designer. Unfortunately there's no way to do it in Java 1.0 or 1.1. It may appear in future versions.

Applet Security

The possibility of surfing the Net, wandering across a random page, playing an applet and catching a virus is a fear that has scared many uninformed people away from Java. This fear has also driven a lot of the development of Java in the direction it's gone. Earlier I discussed various security features of Java including automatic garbage collection, the elimination of pointer arithmetic and the Java interpreter. These serve the dual purpose of making the language simple for programmers and secure for users. You can surf the web without worrying that a Java applet will format your hard disk or introduce a virus into your system.

In fact both Java applets and applications are much safer in practice than code written in traditional languages. This is because even code from trusted sources is likely to have bugs. However Java programs are much less susceptible to common bugs involving memory access than are programs written in traditional languages like C. Furthermore the Java runtime environment provides a fairly robust means of trapping bugs before they bring down your system. Most users have many more problems with bugs than they do with deliberately malicious code. Although users of Java applications aren't protected from out and out malicious code, they are largely protected from programmer errors.

Applets implement additional security restrictions that protect users from malicious code too. This is accomplished through the java.lang.SecurityManager class. This class is subclassed to provide different security environments in different virtual machines. Regrettably implementing this additional level of protection does somewhat restrict the actions an applet can perform. Let's explore exactly what an applet can and cannot do.

What Can an Applet Do?

An applet can:

Anything you can do with these abilities you can do in an applet. An applet cannot:

Who Can an Applet Talk To?

By default an applet can only open network connections to the system from which the applet was downloaded. This system is called the codebase. An applet cannot talk to an arbitrary system on the Internet. Any communication between different client systems must be mediated through the server.

The concern is that if connections to arbitrary hosts were allowed, then a malicious applet might be able to make connections to other systems and launch network based attacks on other machines in an organization's internal network. This would be an especially large problem because the machine's inside a firewall may be configured to trust each other more than they would trust any random machine from the Internet. If the internal network is properly protected by a firewall, this might be the only way an external machine could even talk to an internal machine. Furthermore arbitrary network connections would allow crackers to more easily hide their true location by passing their attacks through several applet intermediaries.

HotJava, Sun's applet viewer, and Internet Explorer (but not Netscape) let you grant applets permission to open connections to any system on the Internet, though this is not enabled by default.

How much CPU time does an applet get?

One of the few legitimate concerns about hostile applets is excessive use of CPU time. It is possible on a non-preemptively multitasking system (specifically the Mac) to write an applet that uses so much CPU time in a tight loop that it effectively locks up the host system. This is not a problem on preemptively multitasking systems like Solaris and Windows NT. Even on those platforms, though, it is possible for an applet to force the user to kill their web browser, possibly losing accumulated bookmarks, email and other work.

It's also possible for an applet to use CPU time for purposes other than the apparent intent of the applet. For instance, a popular applet could launch a Chinese lottery attack on a Unix password file. A popular game applet could launch a thread in the background which tried a random assortment of keys to break a DES encrypted file. If the key was found, then a network connection could be opened to the applet server to send the decrypted key back. The more popular the applet was the faster the key would be found. The ease with which Java applets are decompiled would probably mean that any such applet would be discovered, but there really isn't a way to prevent it from running in the first place.

User Security Issues and Social Engineering

Contrary to popular belief most computer break-ins by external hackers don't happen because of great knowledge of operating system internals and network protocols. They happen because a hacker went digging through a company's garbage and found a piece of paper with a password written on it, or perhaps because they talked to a low-level bureaucrat on the phone, convinced this person they were from the local data processing department and that they needed him or her to change their password to "DEBUG."

This is sort of attack is called social engineering. Java applets introduce a new path for social engineering. For instance imagine an applet that pops up a dialog box that says, "You have lost your connection to the network. Please enter your username and password to reconnect." How many people would blindly enter their username and password without thinking? Now what if the box didn't really come from a lost network connection but from a hacker's applet? And instead of reconnecting to the network (a connection that was never lost in the first place) the username and password was sent over the Internet to the cracker? See the problem?

Preventing Applet Based Social Engineering Attacks

To help prevent this, Java applet windows are specifically labeled as such with an ugly bar that says: "Warning: Applet Window" or "Unsigned Java Applet Window." The exact warning message varies from browser to browser but in any case should be enough to prevent the more obvious attacks on clueless users. It still assumes the user understands what "Unsigned Java Applet Window" means and that they shouldn't type their password or any sensitive information in such a window. User education is the first part of any real security policy.

Content Issues

Some people claim that Java is insecure because it can show the user erotic pictures and play flatulent noises. By this standard the entire web is insecure. Java makes no determination of the content of an applet. Any such determination would require artificial intelligence and computers far more powerful than what we have today.

The Basic Applet Life Cycle

1.      The browser reads the HTML page and finds any <APPLET> tags.

2.      The browser parses the <APPLET> tag to find the CODE and possibly CODEBASE attribute.

3.      The browser downloads the .class file for the applet from the URL found in the last step.

4.      The browser converts the raw bytes downloaded into a Java class, that is a java.lang.Class object.

5.      The browser instantiates the applet class to form an applet object. This requires the applet to have a noargs constructor.

6.      The browser calls the applet's init() method.

7.      The browser calls the applet's start() method.

8.      While the applet is running, the browser passes any events intended for the applet, e.g. mouse clicks, key presses, etc., to the applet's handleEvent() method. Update events are used to tell the applet that it needs to repaint itself.

9.      The browser calls the applet's stop() method.

10.  The browser calls the applet's destroy() method.

The Basic Applet Life Cycle

All applets have the following four methods:

public void init();
public void start();
public void stop();
public void destroy();

They have these methods because their superclass, java.applet.Applet, has these methods. (It has others too, but right now I just want to talk about these four.)

In the superclass, these are simply do-nothing methods. For example,

        public void init() {}

Subclasses may override these methods to accomplish certain tasks at certain times. For instance, the init() method is a good place to read parameters that were passed to the applet via <PARAM> tags because it's called exactly once when the applet starts up. However, they do not have to override them. Since they're declared in the superclass, the Web browser can invoke them when it needs to without knowing in advance whether the method is implemented in the superclass or the subclass. This is a good example of polymorphism.

init(), start(), stop(), and destroy()

The init() method is called exactly once in an applet's life, when the applet is first loaded. It's normally used to read PARAM tags, start downloading any other images or media files you need, and set up the user interface. Most applets have init() methods.

The start() method is called at least once in an applet's life, when the applet is started or restarted. In some cases it may be called more than once. Many applets you write will not have explicit start()methods and will merely inherit one from their superclass. A start() method is often used to start any threads the applet will need while it runs.

The stop() method is called at least once in an applet's life, when the browser leaves the page in which the applet is embedded. The applet's start() method will be called if at some later point the browser returns to the page containing the applet. In some cases the stop() method may be called multiple times in an applet's life. Many applets you write will not have explicit stop()methods and will merely inherit one from their superclass. Your applet should use the stop() method to pause any running threads. When your applet is stopped, it should not use any CPU cycles.

The destroy() method is called exactly once in an applet's life, just before the browser unloads the applet. This method is generally used to perform any final clean-up. For example, an applet that stores state on the server might send some data back to the server before it's terminated. many applets will not have explicit destroy() methods and just inherit one from their superclass.

For example, in a video applet, the init() method might draw the controls and start loading the video file. The start() method would wait until the file was loaded, and then start playing it. The stop() method would pause the video, but not rewind it. If the start() method were called again, the video would pick up where it left off; it would not start over from the beginning. However, if destroy() were called and then init(), the video would start over from the beginning.

In the JDK's appletviewer, selecting the Restart menu item calls stop() and then start(). Selecting the Reload menu item calls stop(), destroy(), and init(), in that order. (Normally the byte codes will also be reloaded and the HTML file reread though Netscape has a problem with this.)

The applet start() and stop() methods are not related to the similarly named methods in the java.lang.Thread class.

Your own code may occasionally invoke start() and stop(). For example, it's customary to stop playing an animation when the user clicks the mouse in the applet and restart it when they click the mouse again.

Your own code can also invoke init() and destroy(), but this is normally a bad idea. Only the environment should call init() and destroy().

The Coordinate System

Java uses the standard, two-dimensional, computer graphics coordinate system. The first visible pixel in the upper left-hand corner of the applet canvas is (0, 0). Coordinates increase to the right and down.

 

Graphics Objects

In Java all drawing takes place via a Graphics object. This is an instance of the class java.awt.Graphics.

Initially the Graphics object you use will be the one passed as an argument to an applet's paint() method. Later you'll see other Graphics objects too. Everything you learn today about drawing in an applet transfers directly to drawing in other objects like Panels, Frames, Buttons, Canvases and more.

Each Graphics object has its own coordinate system, and all the methods of Graphics including those for drawing Strings, lines, rectangles, circles, polygons and more. Drawing in Java starts with particular Graphics object. You get access to the Graphics object through the paint(Graphics g) method of your applet. Each draw method call will look like g.drawString("Hello World", 0, 50) where g is the particular Graphics object with which you're drawing.

For convenience's sake in this lecture the variable g will always refer to a preexisting object of the Graphics class. As with any other method you are free to use some other name for the particular Graphics context, myGraphics or appletGraphics perhaps.

Drawing Lines

Drawing straight lines with Java is easy. Just call

        g.drawLine(x1, y1, x2, y2) 

where (x1, y1) and (x2, y2) are the endpoints of your lines and g is the Graphics object you're drawing with.

This program draws a line diagonally across the applet.

import java.applet.*;   

import java.awt.*;

 

public class SimpleLine extends Applet {

 

 public void paint(Graphics g) {

     g.drawLine(0, 0, this.getSize().width, this.getSize().height);

 }

 

}

Drawing Rectangles

Drawing rectangles is simple. Start with a Graphics object g and call its drawRect() method:

public void drawRect(int x, int y, int width, int height)

As the variable names suggest, the first int is the left hand side of the rectangle, the second is the top of the rectangle, the third is the width and the fourth is the height. This is in contrast to some APIs where the four sides of the rectangle are given.

This uses drawRect() to draw a rectangle around the sides of an applet.

import java.applet.*;   

import java.awt.*;

 

public class RectangleApplet extends Applet {

 

  public void paint(Graphics g) {

    g.drawRect(0, 0, this.getSize().width - 1, this.getSize().height - 1);

  }

 

}

 

Remember that getSize().width is the width of the applet and getSize().height is its height.

Why was the rectangle drawn only to getSize().height-1 and getSize().width-1?

Remember that the upper left hand corner of the applet starts at (0, 0), not at (1, 1). This means that a 100 by 200 pixel applet includes the points with x coordinates between 0 and 99, not between 0 and 100. Similarly the y coordinates are between 0 and 199 inclusive, not 0 and 200.

There is no separate drawSquare() method. A square is just a rectangle with equal length sides, so to draw a square call drawRect() and pass the same number for both the height and width arguments.

Filling Rectangles

The drawRect() method draws an open rectangle, a box if you prefer. If you want to draw a filled rectangle, use the fillRect() method. Otherwise the syntax is identical.

This program draws a filled square in the center of the applet. This requires you to separate the applet width and height from the rectangle width and height. Here's the code:

import java.applet.*;   

import java.awt.*;

 

public class FillAndCenter extends Applet {

  public void paint(Graphics g) {

    int appletHeight = this.getSize().height;

    int appletWidth  = this.getSize().width;

    int rectHeight   = appletHeight/3;

    int rectWidth    = appletWidth/3;

    int rectTop      = (appletHeight - rectHeight)/2;

    int rectLeft     = (appletWidth - rectWidth)/2;

   

    g.fillRect(rectLeft, rectTop, rectWidth-1, rectHeight-1);

  }

}

Clearing Rectangles

It is also possible to clear a rectangle that you've drawn. The syntax is exactly what you'd expect:

public abstract void clearRect(int x, int y, int width, int height)

This program uses clearRect() to blink a rectangle on the screen.

import java.applet.*;   

import java.awt.*;

 

public class Blink extends Applet {

  public void paint(Graphics g) {

    int appletHeight = this.getSize().height;

    int appletWidth  = this.getSize().width;

    int rectHeight   = appletHeight/3;

    int rectWidth    = appletWidth/3;

    int rectTop      = (appletHeight - rectHeight)/2;

    int rectLeft     = (appletWidth - rectWidth)/2;

 

    for (int i=0; i < 1000; i++) {

      g.fillRect(rectLeft, rectTop, rectWidth-1, rectHeight-1);

      g.clearRect(rectLeft, rectTop, rectWidth-1, rectHeight-1);

    }

  }

}

This is not how you should do animation in practice, but this is the best we can do until we introduce threads.

Ovals and Circles

Java has methods to draw outlined and filled ovals. As you'd probably guess these methods are called drawOval() and fillOval() respectively. As you might not guess they take identical arguments to drawRect() and fillRect(), i.e.

public void drawOval(int left, int top, int width, int height) 
public void fillOval(int left, int top, int width, int height)

Instead of the dimensions of the oval itself, the dimensions of the smallest rectangle which can enclose the oval are specified. The oval is drawn as large as it can be to touch the rectangle's edges at their centers. This picture may help:

The arguments to drawOval() are the same as the arguments to drawRect(). The first int is the left hand side of the enclosing rectangle, the second is the top of the enclosing rectangle, the third is the width and the fourth is the height.

There is no special method to draw a circle. Just draw an oval inside a square.

Java also has methods to draw outlined and filled arcs. They're similar to drawOval() and fillOval() but you must also specify a starting and ending angle for the arc. Angles are given in degrees. The signatures are:

public void drawArc(int left, int top, int width, int height, 
   int startangle, int stopangle)
public void fillArc(int left, int top, int width, int height, 
   int startangle, int stopangle)

The rectangle is filled with an arc of the largest circle that could be enclosed within it. The location of 0 degrees and whether the arc is drawn clockwise or counter-clockwise are currently platform dependent.

Bullseye

This is a simple applet which draws a series of filled, concentric circles alternating red and white, in other words a bullseye.

import java.applet.*;

import java.awt.*;

 

public class Bullseye extends Applet {

 

  public void paint(Graphics g) {

    int appletHeight = this.getSize().height;

    int appletWidth = this.getSize().width;

 

    for (int i=8; i >= 0; i--) {

      if ((i % 2) == 0) g.setColor(Color.red);

      else g.setColor(Color.white);

     

      // Center the rectangle

      int rectHeight = appletHeight*i/8;

      int rectWidth  = appletWidth*i/8;

      int rectLeft   = appletWidth/2  - i*appletWidth/16;

      int rectTop    = appletHeight/2 - i*appletHeight/16;

      g.fillOval(rectLeft, rectTop, rectWidth, rectHeight);

    }

  }

 

}

The .class file that draws this image is only 684 bytes. The equivalent GIF image is 1,850 bytes, almost three times larger.

Almost all the work in this applet consists of centering the enclosing rectangles inside the applet. The lines in bold do that. The first two lines just set the height and the width of the rectangle to the appropriate fraction of the applet's height and width. The next two lines set the position of the upper left hand corner. Once the rectangle is positioned, drawing the oval is easy.

Polygons

In Java rectangles are defined by the position of their upper left hand corner, their height, and their width. However it is implicitly assumed that there is in fact an upper left hand corner. Not all rectangles have an upper left hand corner. For instance consider the rectangle below.

Where is its upper left hand corner? What's been assumed so far is that the sides of the rectangle are parallel to the coordinate axes. You can't yet handle a rectangle that's been rotated at an arbitrary angle.

There are some other things you can't handle either, triangles, stars, rhombuses, kites, octagons and more. To take care of this broad class of shapes Java has a Polygon class.

Polygons are defined by their corners. No assumptions are made about them except that they lie in a 2-D plane. The basic constructor for the Polygon class is

public Polygon(int[] xpoints, int[] ypoints, int npoints)

xpoints is an array that contains the x coordinates of the polygon. ypoints is an array that contains the y coordinates. Both should have the length npoints. Thus to construct a 3-4-5 right triangle with the right angle on the origin you would type

int[] xpoints = {0, 3, 0};
int[] ypoints = {0, 0, 4};
Polygon myTriangle = new Polygon(xpoints, ypoints, 3);

To actually draw the polygon you use java.awt.Graphics's drawPolygon(Polygon p) method within your paint() method like this:

        g.drawPolygon(myTriangle);

You can pass the arrays and number of points directly to the drawPolygon() method if you prefer:

      g.drawPolygon(xpoints, ypoints, xpoints.length);

There's also an overloaded fillPolygon() method. The syntax is exactly as you expect:

      g.fillPolygon(myTriangle);
        g.fillPolygon(xpoints, ypoints, xpoints.length());

Java automatically closes the polygons it draws. That is it draws polygons that look like the one on the right rather than the one on the left.

If you don't want your polygons to be closed, you can draw a polyline instead with the Graphics class's drawPolyline() method

        public abstract void drawPolyline(int xPoints[], int yPoints[], int nPoints)

Loading Images

Polygons, ovals, lines and text cover a lot of ground. The remaining graphic object you need is an image. Images in Java are bitmapped GIF or JPEG files that can contain pictures of just about anything. You can use any program at all to create them as long as that program can save in GIF or JPEG format.

Images displayed by Java applets are retrieved from the web via a URL that points to the image file. An applet that displays a picture must have a URL to the image its going to display. Images can be stored on a web server, a local hard drive or anywhere else the applet can point to via a URL. Make sure you put your images somewhere the person viewing the applet can access them. A file URL that points to your local hard drive may work while you're developing an applet, but it won't be of much use to someone who comes in over the web.

Typically you'll put images in the same directory as either the applet or the HTML file. Though it doesn't absolutely have to be in one of these two locations, storing it there will probably be more convenient. Put the image with the applet .class file if the image will be used for all instances of the applet. Put the applet with the HTML file if different instances of the applet will use different images. A third alternative is to put all the images in a common location and use PARAMs in the HTML file to tell Java where the images are.

If you know the exact URL for the image you wish to load, you can load it with the getImage() method:

URL imageURL = new URL("http://www.prenhall.com/logo.gif");
java.awt.Image img = this.getImage(imageURL);

You can compress this into one line as follows

Image img = this.getImage(new URL("http://www.prenhall.com/logo.gif"));

The getImage() method is provided by java.applet.Applet. The URL class is provided by java.net.URL. Be sure to import it.

Code and Document Bases

If you don't know the exact URL of the image, but you do know its name and that it's in the same directory as the applet, you can use an alternate form of getImage() that takes a URL and a filename. Use the applet's getCodeBase() method to return the URL to the applet directory like this:

Image img = this.getImage(this.getCodeBase(), "test.gif");

The getCodeBase() method returns a URL object that points to the directory where the applet came from.

Finally if the image file is stored in the same directory as the HTML file, use the same getImage() method but pass it getDocumentBase() instead. This returns a URL that points at the directory which contains the HTML page in which the applet is embedded.

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

If an image is loaded from the Internet, it may take some time for it to be fully downloaded. Most of the time you don't need to worry about this. You can draw the Image as soon as you've connected it to a URL using one of the above methods. Java will update it as more data becomes available without any further intervention on your part.

Load all the images your applet needs in the init() method. In particular you do not want to load them in the paint() method. If you do they will be reloaded every time your applet repaints itself, and applet performance will be abysmal.

Drawing Images at Actual Size

Once the image is loaded draw it in the paint() method using the drawImage() method like this

        g.drawImage(img, x, y, io)

img is a member of the Image class which you should have already loaded in your init() method. x is the x coordinate of the upper left hand corner of the image. y is the y coordinate of the upper left hand corner of the image. io is a member of a class which implements the ImageObserver interface. The ImageObserver interface is how Java handles the asynchronous updating of an Image when it's loaded from a remote web site rather than directly from the hard drive. java.applet.Applet implements ImageObserver so for now just pass the keyword this to drawImage() to indicate that the current applet is the ImageObserver that should be used.

A paint() method that does nothing more than draw an Image starting at the upper left hand corner of the applet may look like this

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

This draws the image at the actual size of the picture.

Scaling Images

You can scale an image into a particular rectangle using this version of the drawImage() method:

public boolean drawImage(Image img, int x, int y, int width, 
    int height, ImageObserver io) 

width and height specify the size of the rectangle to scale the image into. All other arguments are the same as before. If the scale is not in proportion to the size of the image, it can end up looking quite squashed.

To avoid disproportionate scaling use the image's getHeight() and getWidth() methods to determine the actual size. Then scale appropriately. For instance this is how you would draw an Image scaled by one quarter in each dimension:

g.drawImage(img, 0, 0, img.getWidth(this)/4, img.getHeight(this)/4, this);

This program reads a GIF file in the same directory as the HTML file and displays it at a specified magnification. The name of the GIF file and the magnification factor are specified via PARAMs.

import java.awt.*;

import java.applet.*;

 

public class MagnifyImage extends Applet {

 

  private Image image;

  private int scaleFactor;

 

  public void init() {

    String filename  = this.getParameter("imagefile");

    this.image       = this.getImage(this.getDocumentBase(), filename);

    this.scaleFactor = Integer.parseInt(this.getParameter("scalefactor"));

  }

 

  public void paint (Graphics g) {

    int width    = this.image.getWidth(this);

    int height   = this.image.getHeight(this);

    scaledWidth  = width * this.scaleFactor;

    scaledHeight = height * this.scaleFactor;

    g.drawImage(this.image, 0, 0, scaledWidth, scaledHeight, this);

  }

 

}

This applet is straightforward. The init() method reads two PARAMs, one the name of the image file, the other the magnification factor. The paint() method calculates the scale and then draws the image.

Color

Color is a class in the AWT. Individual colors like red or mauve are instances of this class, java.awt.Color. Be sure to import it if you want to use other than the default colors. You create new colors using the same RGB triples that you use to set background colors on web pages. However you use decimal numbers instead of the hex values used by the bgcolor tag. For example medium gray is Color(127, 127, 127). Pure white is Color(255, 255, 255). Pure red is (255, 0, 0) and so on. As with any variable you should give your colors descriptive names. For instance

Color medGray = new Color(127, 127, 127);
Color cream = new Color(255, 231, 187);
Color lightGreen = new Color(0, 55, 0);

A few of the most common colors are available by name. These are

·