Testen Sie Ihr Applet auf einfache Weise: Verwandeln Sie es in eine Anwendung

OK Sie haben das Hello World-Applet hinter sich und gelangen zu etwas viel Größerem, viel Interessanterem. Sie benötigen noch eine browserbasierte Oberfläche, um Ihr Programm als Applet zu entwickeln. Das Debuggen des Applets durch Einfügen von printlns in Netscape ist jedoch ein Problem, und der Appletviewer scheint nie mehr richtig zu funktionieren. Oder vielleicht gerade schreiben Sie ein Programm , das sowohl als Applet nützlich wäre und als eigenständige Anwendung. Sie können die mainFunktion in Ihre AppletUnterklasse einfügen und Code hinzufügen, um Befehlszeilenargumente, Fenstermanipulationen und das Laden von Bildern selbst zu handhaben, da der Browser AppletContextnicht mehr für Sie da ist.

AppletContextist eine Schnittstelle, sodass Sie nicht einmal ein AppletContextObjekt instanziieren können , um die Funktionen bereitzustellen, die der Browser AppletContextnormalerweise bereitstellt. Sie können aber die Schnittstelle implementieren. Und wenn Sie es sehr allgemein implementiert haben, können Sie es in Ihrer eigenen Werkzeugkiste aufbewahren, um es immer wieder zu verwenden. Dieser Artikel zeigt Ihnen, wie Sie genau das tun. Tatsächlich müssen Sie die Implementierung nicht einmal selbst schreiben, da der Quellcode am Ende dieses Artikels enthalten ist.

Die Klasse und die Schnittstellen

Um unser Ziel der Replikation der browserbasierten Umgebung zu erreichen, müssen wir tatsächlich einige Schnittstellen implementieren - insbesondere AppletContextund AppletStub. AppletContextsoll die Umgebung des Applets darstellen - normalerweise den Browser und das einschließende HTML-Dokument. Das AppletStubwird von der AppletOberklasse verwendet, um die Applet-Funktionen zu implementieren, die Sie aufrufen können, z. B. getAppletContext()und getParameter(). Wir werden auch eine andere Schnittstelle implementieren : URLStreamHandlerFactory. Dies wird später besprochen.

Da wir bisher nur Schnittstellen implementieren, haben wir immer noch die Möglichkeit, etwas zu erweitern. Der Browser stellt das Fenster bereit, in dem das Applet gezeichnet wird. Daher benötigen wir ein Frame-Objekt. Ich habe eine Klasse erstellt, die ich nenne und DummyAppletContextdie erweitert Frame. seine Definition beginnt:

öffentliche Klasse DummyAppletContext erweitert Frame implementiert AppletStub, AppletContext, URLStreamHandlerFactory { 

Initialisierung

Ich habe ein paar Möglichkeiten, a zu instanziieren DummyAppletContext; Eine der nützlichsten ist direkt von einer mainFunktion (wie unten gezeigt) in der DummyAppletContextKlasse selbst. Auf diese Weise ich nicht haben zu definieren , mainnur in jedem Applet es als eigenständige Anwendung ausgeführt werden . Ich kann Applets so wie sie sind über meine ausführen DummyAppletContext.

public static void main (String args []) {neuer DummyAppletContext (args); }}

Der neue Operator oben ruft den Konstruktor auf, der die Argumentliste verwendet. Ich gehe davon aus, dass das erste Argument der Name der AppletUnterklasse ist, und versuche, die Klasse zu instanziieren. Ich benutze die Classstatische Funktion forName(), um das ClassObjekt abzurufen, und rufe dann seine newInstance()Funktion auf, um das Applet zu instanziieren. Sie können eine Vielzahl von Ausnahmen aus dieser einen Zeile abrufen, die alle nicht wiederherstellbar sind. Wenn ich also eine Ausnahme bekomme, drucke ich sie einfach aus und beende sie. Wenn es funktioniert, rufe ich eine private Initialisierungsfunktion auf, die ich in allen Konstruktoren verwende. Hier ist der Code für den Konstruktor:

public DummyAppletContext (String args []) {

super (args [0]);

try {Applet applet = (Applet) Class.forName (args [0]) .newInstance ();

init (Applet, 640, 480, args, 1); } catch (Ausnahme e) {e.printStackTrace (); System.exit (1); }}

Einer der anderen Konstruktoren (siehe unten) verwendet ein vorhandenes Applet-Objekt. Ich verwende diesen Konstruktor, wenn ich die mainFunktion in einer anderen Klasse implementieren möchte , z. B. in der AppletUnterklasse selbst. Eigentlich ist dies nur eine Annehmlichkeit. Mit einer mainFunktion in der AppletUnterklasse kann ich ein Programm starten, indem ich den Java-Interpreter in der AppletUnterklasse ausführe, anstatt ihn ausführen DummyAppletContextund die AppletUnterklasse separat angeben zu müssen ( java MyAppletversus java DummyAppletContext MyApplet). Außerdem kann ich im Applet eine Standardbreite und -höhe angeben. (Ich stelle einen anderen Konstruktor wie diesen zur Verfügung, für den die Standardargumente für Breite und Höhe nicht erforderlich sind.)

public DummyAppletContext (Applet-Applet, int default_width, int default_height, String args []) {

super (applet.getClass (). getName ());

init (Applet, Standardbreite, Standardhöhe, Argumente, 0); }}

Die initFunktion erledigt den größten Teil der Setup-Magie. Zu den Argumenten gehören das Applet-Objekt, die Standardgröße, Befehlszeilenargumente und der Startindex für die Argumente. Denken Sie daran, dass wir das erste Argument in einem der Konstruktoren verwendet haben, um die zu Appletladende Unterklasse nur anhand ihres Namens zu bestimmen . In diesem Fall ist startidxder Index, ab dem die Analyse der Argumente und Parameter des Applets gestartet werden soll, 1, andernfalls jedoch 0. Die initFunktion teilt der URLKlasse zunächst mit, dass dieses Objekt jetzt der Standard ist URLStreamHandlerFactory. (Wir implementieren die Schnittstelle dafür.) Anschließend wird das angegebene Applet einem Vektor von Applets hinzugefügt, der nur dieses eine Applet enthält, und es teilt dem Applet mit, dass dieses Objekt als sein Applet fungiert AppletStub. Hier ist die initFunktion:

private void init (Applet-Applet, int default_width, int default_height, String args [], int startidx) {

URL.setURLStreamHandlerFactory (this);

applets.addElement (Applet); applet.setStub (this);

initial_width = default_width; initial_height = default_height;

parseArgs (args, startidx);

status = new TextField (); status.setEditable (false);

add ("Center", Applet); add ("Süd", Status);

applet.init (); appletResize (initial_width, initial_height);

Show(); applet.start (); }}

The arguments are parsed by simply looping through the array elements and adding every pair of arguments to a hashtable of name/value pairs. The arguments -width and -height are treated specially, and override the default width and height of the applet. They are not added to the hashtable. The argument parsing occurs in the function parseArgs, shown here:

 public void parseArgs( String args[], int startidx ) { for ( int idx = startidx; idx < ( args.length - startidx ); idx+=2 ) { try { if ( args[idx].equals( "-width" ) ) { initial_width = Integer.parseInt( args[idx+1] ); } else if ( args[idx].equals( "-height" ) ) { initial_height = Integer.parseInt( args[idx+1] ); } else { params.put( args[idx], args[idx+1] ); } } catch ( NumberFormatException nfe ) { System.err.println("Warning: command line argument "+args[idx]+ " is not a valid number." ); } } } 

The init function continues by setting up the status area (used by the function showStatus) using an uneditable AWT Text object. It adds the applet and status area components to the frame (the DummyAppletContext) according to the default BorderLayout policy, calls the applet's init function, and resizes the window as specified. Finally, the window is displayed, and the applet's init and start functions are called. (We never have to call stop, and start is never called again since we're not in a browser. Also, I have never used the destroy method for anything, so I don't call it. But if you have a need for it, I would recommend calling it before every System.exit() call, with a test first to see if init() was called.)

I only have to override one Frame function, handleEvent(), as shown below, so I can catch the WINDOW_DESTROY event if the user presses the Close icon on the window bar.

public boolean handleEvent( Event evt ) {

if ( evt.id == Event.WINDOW_DESTROY ) { System.exit(0); }

return super.handleEvent(evt); }

AppletStub

AppletStub

declares a few functions that we have to implement:

  • isActive -- always returns true

  • getDocumentBase -- returns a "file" URL for the current directory

  • getCodeBase -- returns the same thing that getDocumentBase returns

  • getParameter -- indexes the hashtable we built in parseArgs and returns the matching value or null if not there

  • getAppletContext -- returns "this" object (our DummyAppletContext)

  • appletResize -- attempts to resize the window to accomodate a request to resize the applet

Most of these functions are pretty straightforward. However, I did have to do some special things to make getDocumentBase to work the way I wanted it to. I started by creating a reference to a dummy file. Using an object of the File class, I called getAbsolutePath() to get the full path name of the file. For DOS (Windows), I had a file name with a bunch of backslashes in it. My objective was to create a URL, so I had to replace these slashes with forward slashes. Also, the typical browser expects the colon (:) in a DOS filename to be replaced with a vertical bar (|) in the URL. The code below performs a transformation of the dummy file to what appears to be a Netscape-compliant URL.

 public URL getDocumentBase() { URL url = null; try { File dummy = new File( "dummy.html" ); String path = dummy.getAbsolutePath(); if ( ! File.separator.equals( "/" ) ) { StringBuffer buffer = new StringBuffer(); if ( path.charAt(0) != File.separator.charAt(0) ) { buffer.append( "/" ); } StringTokenizer st = new StringTokenizer( path, File.separator ); while ( st.hasMoreTokens() ) { buffer.append( st.nextToken() + "/" ); } if ( File.separator.equals( "\\" ) && ( buffer.charAt(2) == ':' ) ) ' );  else { } path = buffer.toString(); path = path.substring( 0, path.length()-1 ); } url = new URL( "file", "", -1, path ); } catch ( MalformedURLException mue ) { mue.printStackTrace(); } return url; } 

The only other AppletStub function implementation of note is appletResize(). In this function, I not only found that I needed to take into account the size of the status text box, but I also had to accomodate the window decorations (for example, the title bar). Java provides the function needed to get that information in Frame's insets() function. Here is the appletResize function:

public void appletResize( int width, int height ) {

Insets insets = insets();

resize( ( width + insets.left + insets.right ), ( height + status.preferredSize().height + insets.top + insets.bottom ) ); }

AppletContext

The functions required to implement

AppletContext

include:

  • getAudioClip -- returns null, because there doesn't seem to be a toolkit for audio clips in my JDK. (You could handle this differently, returning your own implementation of AudioClip.)

  • getImage -- gets an image from the given URL. For the purposes of the DummyAppletContext, all URLs are assumed to be references to a local file. Therefore getImage converts the URL to a file name, and uses the AWT Toolkit object to load the image.

  • getApplet -- is supposed to return an applet by name. I never name my applet, and there are no other applets, so this always returns null.

  • getApplets -- returns an Enumeration of the applets in this AppletContext. There is only one, so this returns an Enumeration of one element. The Enumeration is created from the Vector we filled in the init function.

  • showDocument -- There are two variations of this function, neither one of which actually shows a document. In a browser, showDocument requests that a document at the given URL be loaded. I actually do show this request in the status area, but I don't attempt to retrieve or show the document.

  • showStatus -- writes the given text to the Text object used as the status area.

The getImage() function uses a private function filenameFromURL() to convert the URL back to a legal file name for the current operating system. Again, I have to make special provisions for DOS, taking into account variations I have seen from time to time. In particular, I have to convert the URL's vertical bar back to a colon.

private String filenameFromURL (URL url) {String filename = url.getFile (); if (Dateiname.charAt (1) == '|') {StringBuffer buf = neuer StringBuffer (Dateiname); buf.setCharAt (1, ':'); Dateiname = buf.toString (); } else if (Dateiname.charAt (2) == '|') {StringBuffer buf = neuer StringBuffer (Dateiname); buf.setCharAt (2, ':'); Dateiname = buf.toString (); } return filename; }}

URLStreamHandlerFactory

URLStreamHandlerFactory

hat nur eine Funktion:

createURLStreamHandler()

. Ich implementiere diese Funktion, um meine Implementierung von zu veranlassen

URLStreamHandler

Wird verwendet, wenn das Applet versucht, eine Verbindung zu einer URL herzustellen. Nun, wenn ich anrufe

openStream()

Auf einer URL in meiner Java-Anwendung wird tatsächlich ein Stream zur Eingabe in die lokale Datei geöffnet. Hier ist

createURLStreamHandler()