Hem E-böcker Specialeffekter och spelutveckling i Java Navigationsmenyer  

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 >>