Deploying Multiple Java Applets as One - MultiApplet
(Page 4 of 5 )
package com.croftsoft.core.gui.multi;
[...]
import com.croftsoft.core.CroftSoftConstants;
import com.croftsoft.core.awt.image.ImageLib;
import com.croftsoft.core.gui.FullScreenToggler;
import com.croftsoft.core.gui.LifecycleWindowListener;
import com.croftsoft.core.lang.NullArgumentException;
import com.croftsoft.core.lang.Pair;
import com.croftsoft.core.lang.lifecycle.Lifecycle;
public class MultiApplet
extends JApplet
implements Lifecycle
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
{
The main class,MultiApplet, extends the Swing version of theApplet class,JApplet. It imports a number of other classes from the CroftSoft reusable code library. It implements theLifecycle interface with itsinit(),start(),stop(), anddestroy()methods so that it can be integrated into a genericLifecycle framework container.
public static final String DEFAULT_NEWS_NAME = "News";
// private final String | appletInfo; |
private final Pair [ ] | appletPairs; |
private final String | newsName; |
private final String | newsHTML; |
private final String | newsPage; |
The instance variableappletInfo is returned when thegetAppletInfo()method is called. VariablesDEFAULT_NEWS_NAME,newsName,newsHTML, andnewsPageare used to create an instance ofMultiAppletNews.
APairis a convenience class from packagecom.croftsoft.core.langthat I use to hold together a related pair ofStringobjects, usually a name-value pair. I like to use an array ofPairinstead of two separateStringarrays because then I do not have to worry about theString arrays accidentally being of unequal length. In theMultiApplet class, I use thenameto store the short name of a game applet to be displayed on a tab at the top of aJTabbedPane. Thevalueis the class name of the corresponding game applet.
private JTabbedPane jTabbedPane;
private Component appletComponent;
private boolean isStarted;
private int index;
private MultiAppletStub multiAppletStub;
ThisJAppletsubclass maintains an instance reference to aJTabbedPaneSwing component. TheJTabbedPaneis used to contain your game applets. When the user clicks a tab, a different game is loaded.
TheappletComponentis a reference to the game currently loaded. Note that it is an instance ofComponent, notApplet. The boolean flagisStartedprovides life-cycle state information. Theindexvariable indicates which tab is currently selected. TheMultiAppletStubinstance is used to propagate theAppletContextto the game applets.
public static void main ( String [ ] args )
//////////////////////////////////////////////////////////////////////
{
launch(
CroftSoftConstants.DEFAULT_APPLET_INFO,
new Pair [ ] {
new Pair ( "Applet1", "javax.swing.JApplet" ),
new Pair ( "Applet2", "javax.swing.JApplet" ) },
DEFAULT_NEWS_NAME,
( String ) null,
CroftSoftConstants.HOME_PAGE,
"CroftSoft MultiApplet",
CroftSoftConstants.FRAME_ICON_FILENAME,
MultiApplet.class.getClassLoader ( ),
( Dimension ) null,
"Close CroftSoft MultiApplet?" );
}
You use static methodmain()to test theMultiApplet from the command line. It passes test data to the followinglaunch()method.
public static void launch (
String appletInfo,
Pair [ ] appletPairs,
String newsName,
String newsHTML,
String newsPage,
String frameTitle,
String frameIconFilename,
ClassLoader frameIconClassLoader,
Dimension frameSize,
String shutdownConfirmationPrompt )
//////////////////////////////////////////////////////////////////////
{
JFrame jFrame = new JFrame ( frameTitle );
try
{
Image iconImage = ImageLib.loadBufferedImage (
frameIconFilename, frameIconClassLoader );
if ( iconImage != null )
{
jFrame.setIconImage ( iconImage );
}
}
catch ( Exception ex )
{
}
The staticlaunch()method launches the program when it is not embedded in a web page as an applet. This allows it to be run as an executable JAR desktop application or as a Java Web Start application. It starts by creating a new frame and setting the frame icon image, that little picture at the top left corner of the frame. If it cannot find the image, it ignores it and moves on.
MultiApplet multiApplet = new MultiApplet (
appletInfo, appletPairs, newsName, newsHTML, newsPage );
jFrame.setContentPane ( multiApplet );
FullScreenToggler.monitor ( jFrame );
LifecycleWindowListener.launchFrameAsDesktopApp (
jFrame,
new Lifecycle [ ] { multiApplet },
frameSize,
shutdownConfirmationPrompt );
}
It then creates an instance of theMultiApplet and sets it as the content page for the frame. MethodFullScreenToggler.monitor(), described in Chapter 5, is used to allow the user to toggle between windowed and full screen mode. The static convenience methodlaunchFrameAsDesktopAppin classLifecycleWindowListenershows the frame and calls the appropriate lifecycle methods on theMultiAppletinstance when the user activates, deactivates, or closes the window.
public MultiApplet (
String appletInfo,
Pair [ ] appletPairs,
String newsName,
String newsHTML,
String newsPage )
//////////////////////////////////////////////////////////////////////
{
NullArgumentException.check ( this.appletInfo = appletInfo );
NullArgumentException.check ( this.appletPairs = appletPairs );
NullArgumentException.check ( this.newsName = newsName );
this.newsHTML = newsHTML;
this.newsPage = newsPage;
}
The constructor method simply saves references to the constructor arguments for later use in theinit()method.
public String getAppletInfo ( )
//////////////////////////////////////////////////////////////////////
{
return appletInfo;
}
public void init ( )
//////////////////////////////////////////////////////////////////////
{
Container contentPane = getContentPane ( );
contentPane.setLayout ( new BorderLayout ( ) );
jTabbedPane = new JTabbedPane (
JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT );
contentPane.add ( jTabbedPane, BorderLayout.CENTER );
Theinit()code makes theJTabbedPanefill the frame. The tabs run along the top and can be scrolled left and right when there are too many to fit in the width of the window. By using the scroll tab layout, you can have a large number of tabs, one for each game, without using up additional screen real estate.
jTabbedPane.add (
new MultiAppletNews ( newsHTML, newsPage, this ), newsName );
On the first tab, I put the special component calledMultiAppletNews. I treat this component differently from the others because it downloads a web page from my web site. I do not want it to be destroyed when a user clicks another tab because it would then have to download the web page again when the user comes back to it. For this reason, as documented in the following code, the code treats this particular panel differently from the others.
for ( int i = 0; i < appletPairs.length; i++ )
{
jTabbedPane.add ( new JPanel ( ), appletPairs [ i ].name );
}
I then create all the other tabs as labeled with the short names of the games.
jTabbedPane.addChangeListener (
new ChangeListener ( )
{
public void stateChanged ( ChangeEvent changeEvent )
{
handleStateChange ( );
}
} );
multiAppletStub = new MultiAppletStub ( this );
}
I add aChangeListenerto theJTabbedPaneso that I can tell when a user clicks a different tab. I end the initialization method by creating aMultiAppletStub instance.
public void start ( )
//////////////////////////////////////////////////////////////////////
{
multiAppletStub.setActive ( true );
try
{
if ( appletComponent instanceof Applet )
{
( ( Applet ) appletComponent ).start ( );
}
else if ( appletComponent instanceof Lifecycle )
{
( ( Lifecycle ) appletComponent ).start ( );
}
}
catch ( Exception ex )
{
ex.printStackTrace ( );
}
isStarted = true;
}
Thestart()method ofMultiAppletdelegates to thestart()method of the currentappletComponentif it can determine that it is an instance ofAppletorLifecycle. Note that to be included withinMultiApplet, a game must extendComponentbut it does not have to extendAppletor implementLifecycle. An example of this might be a game where the updates are driven exclusively by user-input events such as mouse clicks instead of a continuously running animation thread.
public void stop ( )
//////////////////////////////////////////////////////////////////////
{
multiAppletStub.setActive ( false );
try
{
if ( appletComponent instanceof Applet )
{
( ( Applet ) appletComponent ).stop ( );
}
else if ( appletComponent instanceof Lifecycle )
{
( ( Lifecycle ) appletComponent ).stop ( );
}
}
catch ( Exception ex )
{
ex.printStackTrace ( );
}
isStarted = false;
}
public synchronized void destroy ( )
//////////////////////////////////////////////////////////////////////
{
try
{
if ( appletComponent instanceof Applet )
{
( ( Applet ) appletComponent ).destroy ( );
}
else if ( appletComponent instanceof Lifecycle )
{
( ( Lifecycle ) appletComponent ).destroy ( );
}
}
catch ( Exception ex )
{
ex.printStackTrace ( );
}
}
Thestop()anddestroy()methods are similar. Thestart()andstop()methods mutate theactive state of theMultiAppletStub. These methods are usually called in response to windowing events such as window activated or deactivated, window minimized or maximized, or window closed.
private void handleStateChange ( )
//////////////////////////////////////////////////////////////////////
{
if ( isStarted)
{
stop ( );
}
If the user clicks a different tab and the currently selected game is running, thehandleStateChange()method stops it.
if ( index > 0 )
{
jTabbedPane.setComponentAt ( index, new JPanel ( ) );
destroy ( );
appletComponent = null;
System.gc ( );
}
index = jTabbedPane.getSelectedIndex ( );
Anindexposition of zero indicates that the tab panel containingMultiAppletNewsis being displayed. Unless the currentindex position is zero, the currentappletComponentis destroyed and all references to it are removed. A garbage collection of system memory is forced at this point to reduce the chances that it will automatically run later and interrupt the animation of the newly selected game. Theindexposition is updated to point to the newly selected tab panel.
if ( index > 0 )
{
try
{
appletComponent = ( Component ) Class.forName (
appletPairs [ index - 1 ].value ).newInstance ( );
If the newindexposition is not zero, the game is loaded into memory using dynamic linking.
if ( appletComponent instanceof Applet )
{
( ( Applet ) appletComponent ).setStub ( multiAppletStub );
}
if ( appletComponent instanceof JComponent )
{
FullScreenToggler.monitor ( ( JComponent ) appletComponent );
}
jTabbedPane.setComponentAt ( index, appletComponent );
If theappletComponentis an instance ofApplet, theAppletStubis set. If it is an instance ofJComponent, we can monitor it for keyboard events that toggle full screen mode. All the dynamically loaded classes must at least extendComponentso that they can be added to theJTabbedPane.
try
{
if ( appletComponent instanceof Applet )
{
( ( Applet ) appletComponent ).init ( );
}
else if ( appletComponent instanceof Lifecycle )
{
( ( Lifecycle ) appletComponent ).init ( );
}
}
catch ( Exception ex )
{
ex.printStackTrace ( );
}
start ( );
}
catch ( Exception ex )
{
ex.printStackTrace ( );
}
}
}
Theinit()method of the newappletComponentinstance is followed by a call to thestart()method of theMultiApplet. Note that theinit()method of the MultiAppletinitializes theMultiAppletbut itsstart()method initializes theappletComponent.
Next: CroftSoftCollection >>
More Java Articles
More By Apress Publishing
|
This article is excerpted from chapter two of Advanced Java Game Programming, written by David Wallace Croft (Apress; ISBN: 1590591232). Check it out today at your favorite bookstore. Buy this book now.
|
|