Testa la tua applet nel modo più semplice: trasformala in un'applicazione

OK Hai superato l'applet Hello World e sei passato a qualcosa di molto più grande, molto più interessante. Hai ancora bisogno di un'interfaccia basata su browser, quindi svilupperai il tuo programma come applet. Ma il debug dell'applet inserendo printlns in Netscape è un orso, e il visualizzatore di applet sembra non funzionare più correttamente. O forse stai scrivendo un programma che sarebbe utile sia come applet che come applicazione standalone. Si potrebbe inserire la mainfunzione nella vostra Appletsottoclasse, e aggiungere il codice per argomenti maniglia della riga di comando, la manipolazione delle finestre, e il caricamento delle immagini da soli, ora che il browser di AppletContextnon è più lì per voi.

AppletContextè un'interfaccia, quindi non puoi nemmeno istanziare un AppletContextoggetto per fornire le funzioni AppletContextnormalmente fornite dal browser . Ma potresti implementare l'interfaccia. E se lo implementassi in modo molto generico, potresti metterlo da parte nella tua cassetta degli attrezzi per riutilizzarlo più e più volte. Questo articolo mostra come fare proprio questo. In effetti, non devi nemmeno scrivere l'implementazione da solo perché il codice sorgente è incluso alla fine di questo articolo.

La classe e le interfacce

Per raggiungere il nostro obiettivo di replicare l'ambiente basato su browser, dobbiamo effettivamente implementare alcune interfacce, in particolare AppletContexte AppletStub. AppletContextdovrebbe rappresentare l'ambiente dell'applet, normalmente il browser e il documento HTML allegato. Il AppletStubè usato dalla Appletsuperclasse per aiutare a implementare le funzioni dell'applet che puoi chiamare come getAppletContext()e getParameter(). Stiamo per implementare un altra interfaccia pure: URLStreamHandlerFactory. Questo verrà discusso in seguito.

Poiché finora stiamo implementando solo interfacce, abbiamo ancora la possibilità di estendere qualcosa. Il browser fornisce la finestra in cui viene disegnata l'applet, quindi abbiamo bisogno di un oggetto Frame. Ho creato una classe che chiamo DummyAppletContextche si estende Frame; la sua definizione inizia:

la classe pubblica DummyAppletContext estende Frame implementa AppletStub, AppletContext, URLStreamHandlerFactory { 

Inizializzazione

Ho un paio di modi per istanziare un DummyAppletContext; uno dei più utili è direttamente da una mainfunzione (come mostrato di seguito) nella DummyAppletContextclasse stessa. In questo modo, non devo definire mainin alcun applet solo per eseguirlo come applicazione autonoma. Sarò in grado di eseguire applet così come sono, tramite il mio DummyAppletContext.

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

Il nuovo operatore sopra chiama il costruttore che accetta l'elenco di argomenti. Presumo che il primo argomento sia il nome della Appletsottoclasse e cerco di istanziare la classe. Uso la Classfunzione statica forName()per ottenere l' Classoggetto, quindi chiamo la sua newInstance()funzione per istanziare l'applet. Puoi ottenere una serie di eccezioni da questa riga e non sono tutte recuperabili. Quindi, se rilevo un'eccezione, la stampo semplicemente e me ne vado. Se funziona, chiamo una funzione di inizializzazione privata che uso in tutti i costruttori. Ecco il codice per il costruttore:

public DummyAppletContext (String args []) {

super (args [0]);

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

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

Uno degli altri costruttori (mostrati sotto) accetta un oggetto applet esistente. Uso questo costruttore quando voglio implementare la mainfunzione in un'altra classe, come la Appletsottoclasse stessa. In realtà, questa è solo una comodità. Con una mainfunzione nella Appletsottoclasse, posso avviare un programma eseguendo l'interprete Java sulla Appletsottoclasse, invece di doverlo eseguire DummyAppletContexte specificare la Appletsottoclasse separatamente ( java MyAppletrispetto a java DummyAppletContext MyApplet). Mi consente anche di specificare una larghezza e un'altezza predefinite nell'applet. (Fornisco un altro costruttore come questo, che non richiede gli argomenti di larghezza e altezza predefiniti.)

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

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

init (applet, default_width, default_height, args, 0); }

La initfunzione fa la maggior parte della magia di installazione. I suoi argomenti includono l'oggetto applet, la dimensione predefinita, gli argomenti della riga di comando e l'indice iniziale per gli argomenti. Ricorda, abbiamo utilizzato il primo argomento in uno dei costruttori per determinare la Appletsottoclasse da caricare, solo in base al nome. In tal caso, startidxl'indice da cui iniziare l'analisi degli argomenti e dei parametri dell'applet è 1, ma altrimenti è 0. La initfunzione prima dice alla URLclasse che questo oggetto sarà ora il valore predefinito URLStreamHandlerFactory. (Stiamo implementando l'interfaccia per questo.) Quindi aggiunge l'applet dato a un vettore di applet che conterrà solo questa applet e dice all'applet che questo oggetto agirà come suo AppletStub. Ecco la initfunzione:

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

URL.setURLStreamHandlerFactory (questo);

applets.addElement (applet); applet.setStub (questo);

initial_width = default_width; initial_height = default_height;

parseArgs (args, startidx);

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

aggiungi ("Center", applet); aggiungi ("Sud", stato);

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

spettacolo(); applet.start (); }

Gli argomenti vengono analizzati semplicemente scorrendo gli elementi dell'array e aggiungendo ogni coppia di argomenti a una tabella hash di coppie nome / valore . Gli argomenti -width e -altezza vengono trattati in modo speciale, e sostituiscono la larghezza e l'altezza predefinita l'applet. Essi sono non aggiunte alla tabella hash. L'analisi degli argomenti si verifica nella funzione parseArgs, mostrata qui:

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 ("Avvertenza: argomento della riga di comando" + args [idx] + "non è un numero valido."); }}}

La initfunzione prosegue impostando l'area di stato (utilizzata dalla funzione showStatus) utilizzando un oggetto AWT Text non modificabile. Aggiunge l'applet e i componenti dell'area di stato al frame (the DummyAppletContext) in base alla BorderLayoutpolitica predefinita , chiama la initfunzione dell'applet e ridimensiona la finestra come specificato. Infine, viene visualizzata la finestra inite startvengono chiamate le funzioni e l'applet . (Non dobbiamo mai chiamare stope startnon viene mai più chiamato poiché non siamo in un browser. Inoltre, non ho mai usato il destroymetodo per niente, quindi non lo chiamo. Ma se ne hai bisogno, io consiglierei di chiamarlo prima di ogni System.exit()chiamata, con un test prima per vedere se è init()stato chiamato.)

Devo solo sovrascrivere una funzione Frame handleEvent(), come mostrato di seguito, in modo da poter catturare l'evento WINDOW_DESTROY se l'utente preme l'icona Chiudi sulla barra della finestra.

public boolean handleEvent (Event evt) {

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

return super.handleEvent (evt); }

AppletStub

AppletStub

dichiara alcune funzioni che dobbiamo implementare:

  • isActive - restituisce sempre true

  • getDocumentBase - restituisce un URL "file" per la directory corrente

  • getCodeBase- restituisce la stessa cosa che getDocumentBaseritorna

  • getParameter- indicizza la tabella hash che abbiamo creato parseArgse restituisce il valore corrispondente o null se non è presente

  • getAppletContext- restituisce "questo" oggetto (nostro DummyAppletContext)

  • appletResize - tenta di ridimensionare la finestra per accogliere una richiesta di ridimensionamento dell'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 (filename.charAt (1) == '|') {StringBuffer buf = new StringBuffer (filename); buf.setCharAt (1, ':'); nomefile = buf.toString (); } else if (filename.charAt (2) == '|') {StringBuffer buf = new StringBuffer (filename); buf.setCharAt (2, ':'); nomefile = buf.toString (); } return filename; }

URLStreamHandlerFactory

URLStreamHandlerFactory

ha una sola funzione:

createURLStreamHandler()

. Implemento questa funzione per causare la mia implementazione di

URLStreamHandler

da utilizzare ogni volta che l'applet tenta di aprire una connessione a un URL. Adesso, quando chiamo

openStream()

su un URL nella mia applicazione Java, in realtà apre un flusso al file locale per l'input. Qui è

createURLStreamHandler()