Innholdsoversikt for programutvikling

Hvordan lage splash screen

En innvending mot javaprogrammer har vært at de trenger lang tid for å starte opp. Grunnen til at de trenger lang tid er at Javas kjøresystem kan trenge tid til å laste inn alle de nødvendige klassene for et program. Dette kan særlig være tilfelle for swingbaserte vindusprogrammer som kan behøve å hente inn store mengder kode fra klassebiblioteker. Dersom det på skjermen ikke vises noe tegn på at programmet holder på å starte opp, kan brukeren få inntrykk av at det henger, og kan komme til å gjøre gjentatte oppstartforsøk uten å vite noe om første forsøk var vellykket eller ikke. Virkemiddelet mot dette er at det så snart som mulig etter forsøk på programstart kommer opp på skjermen et lite vindu som informerer brukeren om at programmet er i ferd med å starte opp. Et slikt lite vindu kalles på engelsk en splash screen. Jeg kjenner ikke noe godt uttrykk for det på norsk. En mulighet kunne kanskje være oppstartvindu, men da måtte vi i tilfelle ikke blande sammen med startvindu, som er det første "ordentlige" vinduet som programmet viser når det først har rukket å komme skikkelig i gang. I framstillingen nedenfor blir bildet som vises i 'splash screen'-vinduet kalt 'oppstartbilde'.

For at en slik 'splash screen' skal komme opp så fort som mulig, kan imidlertid ikke visningen av den være avhengig av kode som er initiert av programmets main-metode, for main-metoden blir ikke satt i gang før all nødvendig programkode og tilhørende biblioteker er lastet inn. Fra og med javaversjon 6 er dette problemet løst ved at kjøresystemet er gjort i stand til å vise et bilde umiddelbart etter oppstart. Dette oppstartbildet vises i et vindu uten såkalte dekorasjoner, det vil si slikt som ramme, ikon og de vanlige knappene i øverste høyre hjørne. Vi har eksempler på dette ved oppstart av Eclipse og NetBeans. Bildet under viser det 'splash screen' som kommer opp ved oppstart av NetBeans.

Det er mulig å bruke et hvilket som helst gif-, jpg- eller png-bilde som oppstartbilde. Det er også mulig å supplere bildene med animasjon (i gif-bilder), og gjennomsiktighet (gif- eller png-bilder). 'Splash screen'-vinduet vil bli vist midt på skjermen. Det blir automatisk lukket så snart det første, ordinære vinduet som tilhører programmet blir vist på skjermen.

Det finnes to mekanismer for å vise 'splash screen' ved oppstart av et program. Det enkleste alternativet er for oppstart av et program via kommandolinja i et DOS-vindu ved å bruke opsjonen -splash ved oppstart. Dersom class-fila som inneholder programmets main-metode heter MinApplikasjon.class og bildefila for oppstartbildet heter splash.gif og ligger i en katalog bilder under katalogen som inneholder nevnte class-fil, så kan du få startet opp programmet med tilhørende 'splash screen' på følgende måte:

Åpne et vindu for MS-DOS ledetekst og naviger til den katalogen som inneholder class-fila for programmets main-metode. Dersom den heter MinApplikasjon.class, så bruker du følgende DOS-kommando:

   java -splash:bilder/splash.gif MinApplikasjon

Java bruker vanlig skråstrek for å angi underkatalog. Det viser seg at i dette tilfelle, iallfall med Windows 7 og Java 7, så går kommandoen greit både med vanlig skråstrek og med bakoverskråstrek.

En vanlig måte å distribuere javaprogrammer på, er imidlertid å pakke dem i jar-filer. Dette er beskrevet i notatet Distribuering av javaprogrammer. Da kan vi pakke inn bildefila for oppstartbildet i jar-fila sammen med de andre filene som hører til programmet. I tillegg må vi i den tilhørende såkalte manifestfil spesifisere filsti for oppstartbildet. Med samme navnebruk som beskrevet ovenfor ved bruk av DOS-kommando, må manifestfila inneholde følgende kodelinjer:

Main-Class: MinApplikasjon
SplashScreen-Image: bilder/splash.gif

Ved bruk av NetBeans er det svært lett å få lagt inn det nødvendige i både jar-fila og manifestfila. Gå fram på samme måte som det er beskrevet i notatet Distribuering av javaprogrammer når det gjelder bruk av NetBeans. Når du har fått satt main-klasse for prosjektet, kan du i samme dialogboksen for prosjektets 'Properties' klikke på alternativet Application slik du kan se at det er gjort på bildet under. Så bruker du Browse ...-knappen bak tekstfeltet for Splash Screen: til å navigere deg til den bildefila du vil bruke, og så velge den slik at filsti kommer opp i tekstfeltet, slik du ser det har gjort på bildet under.

Dersom programmet er klart til å kjøre så snart kjøresystemet kommer til main-metoden, behøver du ikke å gjøre noe annet enn det som er beskrevet ovenfor når det gjelder bruk av 'splash screen'. Men mange programmer er bygget opp med en såkalt innpluggingsarkitektur på den måten at en liten programkjerne laster inn en serie med innpluggingsprogram ved oppstart. Eclipse og NetBeans er typiske eksempler på dette. I slike tilfeller kan vi på oppstartbildet indikere framdriften av innlastingen. På bildet for NetBeans som er gjengitt ovenfor kan du se at det er gjort ved den røde streken under teksten Loading modules... nederst i bildet.

Programeksempel

Det finnes to muligheter for å få indikert framdrift på en 'splash screen' etter at programmet er kommet så langt som til main-metoden. Det ene alternativet er å tegne direkte på oppstartbildet, det andre er å bytte ut 'splash screen'-vinduet med et vindu uten dekorasjoner (lukkeknapp etc.) og som inneholder oppstartbildet fra 'splash screen'-vinduet i tillegg til en JProgressBar som viser framdriften. Bruk av JProgressBar er beskrevet i notatet Progresjonsindikatorer. Vi skal se på et eksempelprogram som inneholder begge teknikkene. Programmet er hentet fra bind 2 av læreboka Core Java.

For å tegne direkte på oppstartbildet trenger vi en referanse til SplashScreen-objektet og dets grafikk-kontekst. I tillegg trenger vi 'splash screen'-vinduets dimensjoner:

  SplashScreen splash = SplashScreen.getSplashScreen();
  Graphics2D g2 = splash.createGraphics();
  Rectangle grenser = splash.getBounds();

Vi kan nå, ved hjelp av grafikkobjektet g2, utføre hva vi vil av tegneinstruksjoner. Når vi har tegnet, gjør vi kall på SplashScreen-objektets update-metode for å få oppdatert oppstartbildet med det som sist ble tegnet. I vårt tilfelle ønsker vi å tegne en progresjonsbar nederst i bildet, slik det blir vist på bildet nedenfor, der progresjonsbaren er kommet omtrent en firedel bortover, nederst på bildet. Progresjonsbaren er resultat av følgende to instruksjoner, hentet fra programmet som er gjengitt i sin helhet lenger nede.

 27     g.fillRect(x, y, width * percent / 100, height);
 28     splash.update();

Her er width den totale bredden for progresjonsbaren, lik bredden for 'splash screen'-vinduet fratrukket bredden for rammekanten på hver side av progresjonsbaren, mens percent øker fra 0 til 100 i løpet av en løkkegjennomgang. Uttegningen av progressbaren utføres av metoden init1 som er den første som blir kalt opp i programmet som er gjengitt nedenfor.

Merknad

SplashScreen-objektet blir opprettet av kjøresystemet og det finnes bare ett slikt objekt, det er ikke mulig å opprette noe slikt objekt selv. Dersom det ikke på riktig måte er registrert bildefil for oppstartbilde på kommandolinje for DOS-kommando, eller registrert i manifestfil og jar-fil som beskrevet ovenfor, så vil getSplashScreen-metoden returnere null.

Det å tegne direkte på oppstartbildet har sine ulemper. Det er brysomt å måtte beregne alle pikselposisjonene, og den progresjonsbaren vi får tegnet vil ikke helt være i samsvar med den virkelige framdriften. For å unngå disse problemene kan vi, når programmet er kommet til main-metoden, erstatte det opprinnelige 'splash screen'-vinduet (som vises før main-metoden starter) med et oppfølgingsvindu som har samme størrelse som 'splash screen' og inneholder dets oppstartbilde, men med en JProgressBar for framdrift lagt oppå bildet. I et slikt oppfølgingsvindu kan vi også, om vi vil, legge inn hva vi vil av andre swing-komponenter. Denne teknikken er programmert inn i metoden init2 i programmet som er gjengitt nedenfor. Metoden blir kalt opp umiddelbart etter at metoden init1, beskrevet ovenfor, er ferdig med å tegne progresjonsbar direkte på oppstartbildet. Skjermbildet som er gjengitt under viser hva som skjer mens init2 blir utført.

Fullstendig programkode er gjengitt nedenfor og finnes i fila SplashScreenTest.java. Bildefil for oppstartbilde er splash.png. Jar-fil for programmet er corejavasplash.jar.

  1 import java.awt.*;
  2 import java.util.List;
  3 import javax.swing.*;
  4
  5 /**
  6  * This program demonstrates the splash screen API.
  7  *
  8  * @version 1.00 2007-09-21
  9  * @author Cay Horstmann
 10  */
 11 public class SplashScreenTest
 12 {
 13   private static SplashScreen splash;
 14   private static final int DEFAULT_WIDTH = 300;
 15   private static final int DEFAULT_HEIGHT = 300;
 16
 17   private static void drawOnSplash(int percent)
 18   {
 19     Rectangle bounds = splash.getBounds();
 20     Graphics2D g = splash.createGraphics();
 21     int height = 20;
 22     int x = 2;
 23     int y = bounds.height - height - 2;
 24     int width = bounds.width - 4;
 25     Color brightPurple = new Color(76, 36, 121);
 26     g.setColor(brightPurple);
 27     g.fillRect(x, y, width * percent / 100, height);
 28     splash.update();
 29   }
 30
 31   /**
 32    * This method draws on the splash screen.
 33    */
 34   private static void init1()
 35   {
 36     splash = SplashScreen.getSplashScreen();
 37     if (splash == null)
 38     {
 39       System.err.println("Did you specify a splash image with " +
 40               " -splash or in the manifest?");
 41       System.exit(1);
 42     }
 43
 44     try
 45     {
 46       for (int i = 0; i <= 100; i++)
 47       {
 48         drawOnSplash(i);
 49         Thread.sleep(100); // simulate startup work
 50       }
 51     }
 52     catch (InterruptedException e)
 53     {
 54     }
 55   }
 56
 57   /**
 58    * This method displays a frame with the same image as the splash screen.
 59    */
 60   private static void init2()
 61   {
 62     final Image img = Toolkit.getDefaultToolkit().getImage(splash.getImageURL());
 63
 64     final JFrame splashFrame = new JFrame();
 65     splashFrame.setUndecorated(true);
 66
 67     final JPanel splashPanel = new JPanel()
 68     {
 69       public void paintComponent(Graphics g)
 70       {
 71         g.drawImage(img, 0, 0, null);
 72       }
 73     };
 74
 75     final JProgressBar progressBar = new JProgressBar();
 76     progressBar.setStringPainted(true);
 77     splashPanel.setLayout(new BorderLayout());
 78     splashPanel.add(progressBar, BorderLayout.SOUTH);
 79
 80     splashFrame.add(splashPanel);
 81     splashFrame.setBounds(splash.getBounds());
 82     splashFrame.setVisible(true);
 83
 84     new SwingWorker<Void, Integer>()
 85     {
 86       protected Void doInBackground() throws Exception
 87       {
 88         try
 89         {
 90           for (int i = 0; i <= 100; i++)
 91           {
 92             publish(i);
 93             Thread.sleep(100);
 94           }
 95         }
 96         catch (InterruptedException e)
 97         {
 98         }
 99         return null;
100       }
101
102       protected void process(List<Integer> chunks)
103       {
104         for (Integer chunk : chunks)
105         {
106           progressBar.setString("Loading module " + chunk);
107           progressBar.setValue(chunk);
108           splashPanel.repaint(); // because img is loaded asynchronously
109         }
110       }
111
112       protected void done()
113       {
114         splashFrame.setVisible(false);
115
116         JFrame frame = new JFrame();
117         frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
118         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
119         frame.setTitle("SplashScreenTest");
120         frame.setVisible(true);
121       }
122     }.execute();
123   }
124
125   public static void main(String args[])
126   {
127     init1();
128
129     EventQueue.invokeLater(new Runnable()
130     {
131       public void run()
132       {
133         init2();
134       }
135     });
136   }
137 }

Innholdsoversikt for programutvikling

Copyright © Kjetil Grønning, revidert av Eva Hadler Vihovde 2014