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 println
s 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 main
Funktion in Ihre Applet
Unterklasse einfügen und Code hinzufügen, um Befehlszeilenargumente, Fenstermanipulationen und das Laden von Bildern selbst zu handhaben, da der Browser AppletContext
nicht mehr für Sie da ist.
AppletContext
ist eine Schnittstelle, sodass Sie nicht einmal ein AppletContext
Objekt instanziieren können , um die Funktionen bereitzustellen, die der Browser AppletContext
normalerweise 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 AppletContext
und AppletStub
. AppletContext
soll die Umgebung des Applets darstellen - normalerweise den Browser und das einschließende HTML-Dokument. Das AppletStub
wird von der Applet
Oberklasse 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 DummyAppletContext
die 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 main
Funktion (wie unten gezeigt) in der DummyAppletContext
Klasse selbst. Auf diese Weise ich nicht haben zu definieren , main
nur 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 Applet
Unterklasse ist, und versuche, die Klasse zu instanziieren. Ich benutze die Class
statische Funktion forName()
, um das Class
Objekt 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 main
Funktion in einer anderen Klasse implementieren möchte , z. B. in der Applet
Unterklasse selbst. Eigentlich ist dies nur eine Annehmlichkeit. Mit einer main
Funktion in der Applet
Unterklasse kann ich ein Programm starten, indem ich den Java-Interpreter in der Applet
Unterklasse ausführe, anstatt ihn ausführen DummyAppletContext
und die Applet
Unterklasse separat angeben zu müssen ( java MyApplet
versus 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 init
Funktion 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 Applet
ladende Unterklasse nur anhand ihres Namens zu bestimmen . In diesem Fall ist startidx
der Index, ab dem die Analyse der Argumente und Parameter des Applets gestartet werden soll, 1, andernfalls jedoch 0. Die init
Funktion teilt der URL
Klasse 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 init
Funktion:
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 truegetDocumentBase
-- returns a "file" URL for the current directorygetCodeBase
-- returns the same thing thatgetDocumentBase
returnsgetParameter
-- indexes the hashtable we built inparseArgs
and returns the matching value or null if not theregetAppletContext
-- returns "this" object (ourDummyAppletContext
)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 theDummyAppletContext
, 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 thisAppletContext
. There is only one, so this returns an Enumeration of one element. The Enumeration is created from the Vector we filled in theinit
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 theText
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()