Specialeffekter och spelutveckling
i Java(TM) - Navigationsmenyer
av Anibal Wainstein
6.2 Navigationsmenyer
Menyer
är den typ av applets som har varit populärast.
Det var företaget OpenCube
som först insåg fördelarna med menyappletmarknaden.
Idag är det många andra företag som har upptäckt
intresset som finns för appletmenyer. I det här
delkapitlet så skall vi lära oss att göra
menyer med de kunskaper som vi har fått.
6.2.1 Knappmenyn, den enklaste
av alla menyer (ButtonMenu)
Knappen
som vi gjorde i förra avsnittet går att generalisera
till en meny, där varje knapp är ett menyval. Vi
börjar med deklarationerna:
import java.applet.*;
import java.awt.*;
import java.net.*;
public class buttonmenu extends Applet {
//"maxitems" får hålla reda på hur
//många menyknappar användaren har
//specifierat.
public int maxitems;
//Nu när vi har flera knappar så
//måste vi göra om alla bildvariabler
//till arrayer.
public Image currentimage[],normalimage[],mouseoverimage[];
//Nu lägger vi till ännu en bild för att
//visa att besökaren har klickat på en knapp.
public Image mousedownimage[];
//Arrayen "urls" får hålla reda på
//adressen för varje knapp.
//"target" är målramen.
public String urls[],target;
//Vi slipper inte ifrån dubbelbuffringen
//den här gången, nu när vi ritar ut
//flera bilder. Dimension behövs också.
//Vi bör också låta användaren
//specifiera en bakgrundsfärg och
//bakgrundsbild.
public Image bufferimage;
public Graphics bufferg;
public Color backgroundcolor;
public Image backgroundimage=null;
public Dimension d;
//"currentitem" kommer att användas
//för att hålla reda på vilken
//knapp som precis har valts.
public int currentitem=0;
Observera
att trådar behövs inte här. Som du ser så
har vi nu gjort om bildvariablerna så att de är
arrayer istället. Nu har vi ju fler knappar än bara
en att hålla reda på. Vi har dessutom lagt till
ännu en array för att lagra bilden som visas när
besökaren håller ner en knapp. Variabeln "maxitems"
får hålla reda på hur många menyval
det finns. Den indikerar alltså storleken för "currentimage",
"normalimage", "mouseoverimage", "mousedownimage"
och variabeln "urls" som används för att
lagra adresserna för varje menyval. Variabeln "target"
har samma syfte som i Button appleten. Nu måste vi också
ta hänsyn till att användaren kanske vill ha en
bakgrundsfärg och bakgrundsbild. Nu när vi arbetar
med flera bilder så måste vi också använda
dubbelbuffring, därför har vi lagt till variabler
för att hantera allt detta. Slutligen kommer variabeln
"currentitem" att användas för att indikera
vilket menyval som besökaren pekar på just nu.
Metoden init() ser ut så här:
public void init()
{
MediaTracker tracker=new MediaTracker(this);
//Vi har tidigare använt oss
//av en "maxitems" parameter för
//att specifiera antal bilder eller
//meddelanden. Nu så kommer vi istället att
//söka oss fram. While-slingan kommer
//att leta efter parametern "normalimage0",
//"normalimage1", "normalimage2" osv tills
//den inte hittar fler sådana parametrar.
//Samtidigt så inkrementeras "maxitems" för
//att hållar räkningen.
maxitems=0;
while (getParameter("normalimage"+maxitems)!=null) maxitems++;
normalimage=new Image[maxitems];
currentimage=new Image[maxitems];
mouseoverimage=new Image[maxitems];
mousedownimage=new Image[maxitems];
urls=new String[maxitems];
//for-slingan laddar in alla bilder. for (int i=0; i<maxitems; i++) { //Observera tricket vi använder för att //lägga in bilderna i spåraren. normalimage[i]=getImage(getDocumentBase(),getParameter("normalimage"+i)); tracker.addImage(normalimage[i],i*3); mouseoverimage[i]=getImage(getDocumentBase(),getParameter("mouseoverimage"+i)); tracker.addImage(mouseoverimage[i],i*3+1); mousedownimage[i]=getImage(getDocumentBase(),getParameter("mousedownimage"+i)); tracker.addImage(mousedownimage[i],i*3+2); currentimage[i]=normalimage[i];
//Vi passar på att ladda in adresserna
//och lagrar dem i "urls".
urls[i]=getParameter("url"+i);
}
try {tracker.waitForAll();}
catch(InterruptedException e)
{
System.out.println("Någonting stoppade inladdningen...");
}
//Targetparametern laddas in...
target=getParameter("target");
//Här kommer koden som hanterar
//bakgrundsfärgen och bakgrundsbilden.
//Det är bara att klistra in raderna
//från textscroller3 exemplet.
backgroundcolor=new Color(getIntegerParameter("backgroundcolor",16));
String backimage=getParameter("backgroundimage");
if (backimage!=null &&
(backimage.toUpperCase().indexOf("JPG")>-1 ||
backimage.toUpperCase().indexOf("GIF")>-1))
{
backgroundimage=getImage(getDocumentBase(),backimage);
tracker.addImage(backgroundimage,0);
try {tracker.waitForAll();}
catch(InterruptedException e) {}
}
//Slutligen måste vi initiera bufferten.
//Det är bara att klistra in raderna
//från textscroller3 exemplet.
d=size();
bufferimage=createImage(d.width,d.height);
bufferg=bufferimage.getGraphics();
}
Förut
så har vi tvingat appletanvändaren att specifiera
en "maxitems" parameter. Nu så tar vi reda
på hur många menyval det finns genom att testa
hur många bilder som har specifierats med hjälp
av getParameter(). Kommer du ihåg när jag sade
att getParameter() lämnade tillbaka värdet null,
om parametern som den anropades med inte fanns? Det är
detta som vi nu utnyttjar i while-slingan. När det är
gjort så används for-slingan till att ladda in
bilderna och lägga in dom i spårarens lista, samtidigt
som vi passar på att lagra parametervärderna för
adresserna. Observera att om användaren inte specifierar
samma antal bilder och parametrar så blir det problem
och fel, men han får skylla sig själv i så
fall om han inte följer dina instruktioner. Efter att
ha laddat in bilderna så klistrar vi in koden för
att hantera bakgrundsbilder och för att initiera bufferten
(vi tar raderna från init() metoden på TextScroller3).
Metoderna getIntegerParameter() och gotoURL() måste
klistras in efter det. Du måste också skriva över
update() metoden (klistra in den som fanns på TextScroller3).
Metoden paint() ser likt ut den som vi hade i TextScroller3
men med lite ändringar:
public void paint(Graphics g)
{
if (bufferg!=null)
{
if (backgroundimage!=null)
{
bufferg.drawImage(backgroundimage,0,0,this);
}
else
{
bufferg.setColor(backgroundcolor);
bufferg.fillRect(0,0,d.width,d.height);
}
//Slingan ser till att alla knappar
//ritas ut.
for (int i=0; i<maxitems; i++) { //Följande rad används för att centrera //knappen horizontellt. int dx=(d.width-currentimage[i].getWidth(this))/2;
//Följande rad används för att centrera
//knappen vertikalt.
int dy=(d.height/maxitems-currentimage[i].getHeight(this))/2;
//Observer att eftersom menyn är en vertikal
//meny så ser vi till att det finns ett
//steg på (d.height/maxitems) innan nästa
//knapp ritas ut.
bufferg.drawImage(currentimage[i],dx,dy+i*d.height/maxitems,this);
}
g.drawImage(bufferimage,0,0,this);
}
}
Här
ovan så använder vi for-slingan för att rita
ut varje knapp. Observera att menyn är vertikalt, därför
så har vi delat upp applethöjden i "maxitems"
delar (d.height/maxitems) och multiplicerar den här siffran
med variabeln "i". Variabeln "maxitems"
indikerar hur många knappar som har specifierats. Vad
som kan vara förvirrande är beräkningen av
variablerna "dx" och "dy". Dessa variabler
används för att centrera knappen i det lilla området
som den finns i. Följande bild förklarar hur det
fungerar:
Här kan du se på de områden
som varje variabel indikerar i ButtonMenus paint() metod.
Variabeln
"dx" räknas ut genom att dra bort bredden på
knappbilden (getWidth(this)), från bredden på
appleten (d.width) och dela resultatet med två. Variabeln
"dy" räknas ut på samma sätt men
genom att istället dra bort höjden på knappbilden
med höjden på appletområdet, fast här
så är "appletområdet" bara d.height/maxitems.
Dessa två variabler läggs till x- resp. y-positionerna
för knappbilden. Dessutom lägger vi på termen
i*d.height/maxitems så att knapparna ritas ut efter
varandra. Arrayen "currentimage" är bara en
pekararray där vi lagrar bilderna som just nu visas på
appletskärmen. Varje element på "currentimage"
initierades till att peka på "normalimage"
elementen i init() metoden. Detta betyder att besökaren
kommer att se de normala bilderna när appleten för
startas.
Nu är rutinerna för att rita ut bilderna färdiga,
men vi måste också lägga in rutiner för
att ändra på bilderna i "currentimage"
arrayen. Vi börjar med mouseMove() metoden, som räknar
ut var besökaren befinner sig med muspekaren och byter
bild på det området.
public boolean mouseMove(Event e, int x, int y)
{
//När muspekaren kommer in i appleten
//så måste vi räkna ut vilken knapp
//som han pekar på. Vi vet att
//området som varje knapp "innehar", är
//(d.height/maxitems). Så vi delar
//muspekarens y-parameter med det
//här värdet, och vi får ett index
//som vi kan använda.
currentitem=y/(d.height/maxitems);
//Först måste vi undersöka att innehållet
//i "currentitem" inte ger oss en
//ArrayOutOfBoundsException.
if (currentitem>=0 && currentitem<maxitems) { //"nollställ" alla knappar så att //de pekar på den normala bilden. for (int i=0; i<maxitems; i++) currentimage[i]=normalimage[i]; currentimage[currentitem]=mouseoverimage[currentitem]; }
//update() ser till att skärmen
//updateras efter att bilden har ändrats.
update(getGraphics());
return true;
}
Här
kanske det klurigaste är hur vi beräknar vilken
knapp som användaren pekar på. Vi delar muspekarens
y-position med kvoten (d.height/maxitems). Detta ger en array-index
till den knapp som besökaren pekar på. Då
är det ju bara att ändra "currentimage"
pekaren i den indexen så att den pekar på motsvarande
"mouseroverimage"! Men innan vi gör det så
måste vi "nollställa" arrayen "currentimage"
så att alla element åter pekar på "normalimage",
för vi vet ju inte vilken av elementen som kan ha varit
ändrad förut.
När muspekaren hamnar utanför appletområdet,
dvs mouseExit() anropas, så kan det också vara
en bra idé att nollställa "currentimage"
igen. På det sättet så återgår
menyn till sin ursprungliga status när besökaren
tappar intresset för den.
public boolean mouseExit(Event e, int x, int y) { //Om besökaren lämnar appletområdet så //måste "currentimage" arrayen nollställas. for (int i=0; i<maxitems; i++) currentimage[i]=normalimage[i]; update(getGraphics()); return true; }
Det
sista som vi måste göra nu är att se till
att appleten exekverar adressen när besökaren klickar
på en knapp. Kommer du ihåg att vi i mouseMove()
metoden satte "currentitem" till att peka på
den knapp som muspekaren pekar på just nu? Nu har vi
nytta av den här globala variabeln i mouseDown() metoden:
public boolean mouseDown(Event e, int x, int y)
{
//Om användaren klickar på en knapp
//så måste vi först ändra bilden.
currentimage[currentitem]=mousedownimage[currentitem];
update(getGraphics());
//Nu exekverar vi adressen relaterat till
//det menyvalet.
gotoURL(urls[currentitem],target);
return true;
}
När
besökaren klickar på en knapp så sätts
elementet i "currentimage" arrayen att peka på
motsvarande element i "mousedownimage". Skärmen
uppdateras och sedan exekveras adressen för menyvalet
med hjälp av gotoURL() metoden (jag hoppas du kom ihåg
att klistra in den). HTML parametrarna för appleten ser
ut så här:
<APPLET CODE="buttonmenu.class" WIDTH=200 HEIGHT=200> <PARAM name="backgroundcolor" value="000000"> <PARAM name="backgroundimage" value="background2.jpg"> <PARAM name="normalimage0" value="normalbutton0.jpg"> <PARAM name="normalimage1" value="normalbutton1.jpg"> <PARAM name="normalimage2" value="normalbutton2.jpg"> <PARAM name="normalimage3" value="normalbutton3.jpg"> <PARAM name="mouseoverimage0" value="mouseoverbutton0.jpg"> <PARAM name="mouseoverimage1" value="mouseoverbutton1.jpg"> <PARAM name="mouseoverimage2" value="mouseoverbutton2.jpg"> <PARAM name="mouseoverimage3" value="mouseoverbutton3.jpg"> <PARAM name="mousedownimage0" value="mousedownbutton0.jpg"> <PARAM name="mousedownimage1" value="mousedownbutton1.jpg"> <PARAM name="mousedownimage2" value="mousedownbutton2.jpg"> <PARAM name="mousedownimage3" value="mousedownbutton3.jpg"> <PARAM name="url0" value="http://www2.passagen.se/javahuset/"> <PARAM name="url1" value="http://www2.passagen.se/javahuset/"> <PARAM name="url2" value="http://www2.passagen.se/javahuset/"> <PARAM name="url3" value="http://www2.passagen.se/javahuset/"> <PARAM name="target" value=""> </APPLET>
Som
du ser så kan det bli mycket parametrar på en
applet efter ett tag. Jag kommer i senare kapitel att berätta
hur man gör om appletarna till Visual Applet Configurator
plugin, som då förenklar appletkonfigurationen
betydligt. Observera att vi nu har lagt till "backgroundimage"
och "backgroundcolor" parametrarna. Bilden som används
som bakgrundsbild måste förstås ha samma
dimensioner som appleten, 200x200 pixels.
ButtonMenu
är nu helt klart, klicka
här för att titta på exemplet.
Nästa sida >>
|