Kapitel 3 Swing og grafik

Du læser en gammel bog

Den bog, du læser her, er fra 1998, og mange ting kan have ændret sig siden da.
Vi håber, at du stadig kan finde relevant information i den.
Hvis du vil læse aktuelle oplysninger om de avancerede dele af Java, anbefaler vi
bogen Core Java – Advanced Features

De to forrige kapitler har gennemgået de fleste af komponenterne i Swing. I dette kapitel vil det blive gennemgået, hvordan man kan forbedre udseende af sin Swing-brugergrænseflade ved at tilføje grafik til de forskellige komponenter.

 

Ikoner

 

Ikonerne er den mest grundlæggende del af grafikken i Swing. Et ikon repræsenteres som en instans af en klasse, der implementerer interfacet Icon:

 

public interface Icon

{

public int getIconHeight ();

public int getIconWidth ();

public void paintIcon (java.awt.Component, java.awt.Graphics, int, int);

}

 

Icon-interfacet implementeres blandt andet af klassen ImageIcon:

 

public class ImageIcon extends Object implements Icon, Serializable

{

public java.awt.swing.ImageIcon();

public java.awt.swing.ImageIcon(java.awt.Image);

public java.awt.swing.ImageIcon(java.awt.Image, java.lang.String);

public java.awt.swing.ImageIcon(java.lang.String);

public java.awt.swing.ImageIcon(java.lang.String, java.lang.String);

public java.awt.swing.ImageIcon(java.net.URL);

public java.awt.swing.ImageIcon(java.net.URL, java.lang.String);

public java.awt.swing.ImageIcon(byte[]);

public java.awt.swing.ImageIcon(byte[], java.lang.String);

 

public java.lang.String getDescription ();

public int getIconHeight ();

public int getIconWidth ();

public java.awt.Image getImage ();

public int getImageLoadStatus ();

public java.awt.image.ImageObserver getImageObserver ();

public synchronized void paintIcon (java.awt.Component, java.awt.Graphics, int, int);

 

public void setDescription (java.lang.String);

public void setImage (java.awt.Image);

public void setImageObserver (java.awt.image.ImageObserver);

}

 

Klassen har – som det ses – mange constructors. Mange af constructorerne tager en tekststreng med som parameter – det er en beskrivelse af ikonet. Den første opretter et uinitialiseret objekt, mens de to næste opretter et ikon på baggrund af et billede. Dernæst kommer to constructors, der opretter et ikon på baggrund af en URL. De to sidste konverterer et byte-array til et ikon.

ImageIcon-klassen består hovedsageligt af set- og get-metoder. Den mest interessante metode er nok getImageLoadStatus. Den kan bruges, hvis man indlæser ikonet over en langsom forbindelse – for eksempel Internet. Funktionen returnerer en int, der angiver, om billedet er indlæst. De mulige returværdier, der er statiske konstanter i java.awt.MediaTracker, er:

 

  • ABORTED
    Indlæsningen af ikonet er blevet afbrudt.
  • ERRORED
    Der opstod en fejl under indlæsningen af ikonet.
  • COMPLETE
    Ikonet er indlæst.

 

Brug af ikoner

 

De forrige to kapitler har vist, hvordan man kan bruge de forskellige Swing-komponenter. Dette afsnit opsummerer på de to forrige kapitler og viser, hvordan man tilføjer grafik til forskellige Swing-komponenter

 

Knapper

 

Ikonerne kan bruge flere forskellige steder. For eksempel behøver knapper ikke at være så kedelige. Man kan erstatte teksten med et ikon eller vælge at have et ikon ved siden af teksten. Det angives alt sammen i Jbutton’s constructor. Nedenstående eksempel viser, hvordan man indlæser ikoner, og hvordan man tilføjer dem til knapper. Eksemplet resulterer i det billede, der ses i figur 3.1.

 

import java.awt.swing.*;

 

public class JavaMenu extends JFrame

{

private static JavaMenu me;

 

public static final void main(String args[])

{

me = new JavaMenu();

me.show();

 

}

 

public JavaMenu()

{

super(“Java menu”);

 

ImageIcon javaIcon = null;

 

 

javaIcon = new ImageIcon(“javalogo.gif”);

 

JButton textButton = new JButton(“Start Java”);

JButton imageButton = new JButton(javaIcon);

JButton comboButton = new JButton(“Start Java”, javaIcon);

 

textButton.setBounds(100, 20, 200, 100);

imageButton.setBounds(100, 130, 200, 100);

comboButton.setBounds(100, 240, 200, 100);

setSize(400,380);

 

getContentPane().setLayout(null);

getContentPane().add(textButton);

getContentPane().add(imageButton);

getContentPane().add(comboButton);

}

}

 

 

Fig. 3.1 – Med Swing kan man sætte ikoner på knapper.

 

Man kan imidlertid også anvende ikonerne mere kreativt. Man kan for eksempel ændre ikonet, når brugeren bevæger musepilen henover knappen. Det gøres ved at lave et nyt ikon, og tilknytte det knappen ved at kalde metoden setRolloverImage. For at få det til at virke, skal man også vælge

 

at aktivere roll over – det gøres med metodekaldet setRolloverEnabled(true).

 

 

 

 

Menuer

 

Knapper er imidlertid ikke det eneste sted, man kan bruge ikoner. Hvis man har en menu, kan man tilføje ikoner til nogle af menupunkterne. Dette kan man gøre for at vise brugeren, hvilken knap på værktøjslininen, vedkommende kunne have klikket på for at aktivere funktionen. Til det formål skal man bruge klasserne, JMenuItem, JMenu og JMenuBar. Disse har stort set den samme funktionalitet som de tilsvarende klasser fra Abstract Window Toolkit: MenuItem, Menu og MenuBar. Ikonet tilknyttes menupunktet allerede i constructoren. JMenuItem har to constructors, der kan tilknytte et ikon til menupunktet. Den ene har kun en parameter – ikonet. Den anden har både den tekst, der skal vises på menupunktet og ikonet som parametre – i nævnte rækkefølge. Nedenstående eksempel viser, hvordan man kan vise billeder ved siden af menupunkter.

 

import java.awt.swing.*;

 

public class GraphicMenu extends JFrame

{

private static GraphicMenu me;

 

private JMenuBar menu;

private JMenu file;

private JMenuItem open, save;

 

public GraphicMenu()

{

super(“Grafisk menu”);

 

open = new JMenuItem (“Åbn”, new ImageIcon(“open.gif”));

save = new JMenuItem (“Gem”, new ImageIcon(“save.gif”));

 

file = new JMenu(“Filer”);

file.add(open);

file.add(save);

 

menu = new JMenuBar();

menu.add(file);

 

setJMenuBar(menu);

setSize(400, 200);

}

 

public static final void main(String args[])

{

me = new GraphicMenu();

me.show();

 

}

}

 

Udførsel af ovenstående program resulterer i det skærmbillede, der ses i figur 3.2.

 

 

Fig. 3.2 – menupunkter med ikoner.

 

 

 

 

 

Afkrydsningsfelter

 

Ligesom ikoner kan tilknyttes knapper og menupunkter, kan de også tilknyttes afkrydsningsfelter (checkbokse). Det gøres ligeledes i constructoren. Der findes en del constructors til klassen JcheckBox. I listen over parametre skal ikonet indsættes efter den tekst, der skal vises ved siden af afkrydsningsfeltet, og inden den boolean som angiver, om afkrydsningfeltet skal være markeret eller ej. Ved de andre komponenter, der er blevet gennemgået i dette kapitel, har det været nok med et billede til hvert komponent. Sådan er det imidlertid ikke ved afkrydsningsfelter. Her skal brugeren kunne se, om afkrydsningsfeltet er markeret eller ej – derfor skal der tilføjes et ekstra billede.

 

Følgende program viser forskellige måder at lave afkrydsningsfelter med ikoner på:

 

import java.awt.swing.*;

 

public class CheckBoxFrame extends JFrame

{

private static CheckBoxFrame me;

 

public CheckBoxFrame()

{

super(“Vindue med afkrydsningsfelter”);

 

JCheckBox ko = new JCheckBox(“Ko”, new ImageIcon(“ko.gif”), true);

ko.setSelectedIcon(new ImageIcon(“komrk.gif”));

ko.setBounds(70,20,100,30);

 

JCheckBox tiger = new JCheckBox(“Tiger”, new ImageIcon(“tiger.gif”));

tiger.setSelectedIcon(new ImageIcon(“tigermrk.gif”));

 

tiger.setBounds(70,80,100,30);

 

getContentPane().setLayout(null);

setSize(240, 160);

 

getContentPane().add(ko);

getContentPane().add(tiger);

}

 

public static final void main(String args[]) throws Exception

{

UIManager.setLookAndFeel(“java.awt.swing.plaf.windows.WindowsLookAndFeel”);

me = new CheckBoxFrame();

me.show();

}

}

 

 

Figur 3.3 viser, hvordan skærmbilledet kommer til at se ud, når programmet startes. I eksemplet bruges rødere billeder til at vise, at et felt er markeret – i dette tilfælde er  koen markeret, mens tigeren er umarkeret – som ved start af programmet.

 

 

Fig. 3.3 – Afkrydsningsfelter med ikoner.

 

 

 

Faneblade

 

Også fanebladene – repræsenteret af klassen JTabbedPane – kan udstyres med ikoner. Her skal man angive ikonet, når man tilføjer komponentet til panelet. Ikonerne bliver placeret til venstre for teksten på fanerne. Hvis man vil, kan man fuldstændigt nøjes med ikoner. Figur 3.4 viser, hvordan ikoner på faneblade ser ud – kildeteksten til eksemplet ses nedenfor.

 

import java.awt.swing.*;

import java.awt.*;

 

public class SwingTab extends JFrame

{

private static SwingTab me;

 

private JTabbedPane panel;

 

private JPanel java, cpp;

 

 

private JButton javaButton, cppButton;

 

private ImageIcon javaIcon, cppIcon;

 

public SwingTab()

{

super(“Faneblade i Swing”);

 

java = new JPanel(null);

javaButton = new JButton (“Start Java”);

javaButton.setBounds(10,10,370,20);

java.add(javaButton);

 

cpp = new JPanel(null);

cppButton = new JButton(“Start C++”);

cppButton.setBounds(10,10,370,20);

cpp.add(cppButton);

 

javaIcon = new ImageIcon(“smiley.gif”);

cppIcon = new ImageIcon(“frown.gif”);

 

panel = new JTabbedPane();

panel.addTab(“Java”, javaIcon, java);

panel.addTab(“C++”, cppIcon, cpp);

 

setSize (400,300);

getContentPane().add(panel);

}

 

public static final void main(String args[])

{

me = new SwingTab();

me.show();

}

}

 

 

 

Fig. 3.4 – Ikoner på faneblade.

 

 

 

 

 

 


Labels

 

I Abstract Window Toolkit er labels kedelige. De er bare tekststrenge, der kan vises i et vindue. Sådan er det ikke i Swing. Her kan man – udover tekst – have et ikon på en label. Man kan også vælge kun at have et ikon, og på den måde er labels faktisk den nemmeste måde at få vist et ikon i et vindue. Labels i Swing er repræsenteret som objekter af klassen JLabel. Nedenstående eksempel viser, hvordan man kan bruge et JLabel-objekt til at vise et ikon i et vindue.

 

import java.awt.swing.*;

 

public class LabelIcon extends JFrame

{

private static LabelIcon me;

 

private ImageIcon javalogo;

private JLabel javaLabel;

 

public LabelIcon()

{

super(“Java logo”);

 

javalogo = new ImageIcon(“javalogo.gif”);

javaLabel = new JLabel(javalogo);

 

setSize(150,150);

getContentPane().add(javaLabel);

}

 

public static final void main(String args[])

{

me = new LabelIcon();

me.show();

}

}

 

Træer

 

Det er lidt mere kompliceret at bruge sine egne ikoner i Swings træer. Her skal man bruge den klasse, der hedder DefaultTreeCellRenderer. Denne klasse har følgende signatur:

 

public class DefaultTreeCellRenderer extends com.sun.java.swing.JLabel implements com.sun.java.swing.tree.TreeCellRenderer

{

public com.sun.java.swing.tree.DefaultTreeCellRenderer();

 

public java.awt.Color getBackgroundNonSelectionColor();

public java.awt.Color getBackgroundSelectionColor();

public java.awt.Color getBorderSelectionColor();

 

public com.sun.java.swing.Icon getClosedIcon();

public com.sun.java.swing.Icon getDefaultClosedIcon();

public com.sun.java.swing.Icon getDefaultLeafIcon();

public com.sun.java.swing.Icon getDefaultOpenIcon();

public com.sun.java.swing.Icon getLeafIcon();

public com.sun.java.swing.Icon getOpenIcon();

public java.awt.Dimension getPreferredSize();

public java.awt.Color getTextNonSelectionColor();

public java.awt.Color getTextSelectionColor();

public java.awt.Component getTreeCellRendererComponent(com.sun.java.swing.JTree, java.lang.Object, boolean, boolean, boolean, int, boolean);

public void paint(java.awt.Graphics);

public void setBackground(java.awt.Color);

public void setBackgroundNonSelectionColor(java.awt.Color);

public void setBackgroundSelectionColor(java.awt.Color);

public void setBorderSelectionColor(java.awt.Color);

public void setClosedIcon(com.sun.java.swing.Icon);

public void setFont(java.awt.Font);

public void setLeafIcon(com.sun.java.swing.Icon);

public void setOpenIcon(com.sun.java.swing.Icon);

public void setTextNonSelectionColor(java.awt.Color);

public void setTextSelectionColor(java.awt.Color);

}

 

Klassen indeholder en række metoder, der kan bruges til at hente oplysninger om de aktuelle indstillinger. Derudover er der en række metoder, som kan bruges til at ændre dem.

 

Hvis man vil ændre udseendet af et træ i Swing, skal man bruge følgende fremgangsmåde:

 

1.   Opret en instans af DefaultTreeCellRenderer.

2.   Tilpas objektet ved hjælp af klassens set-metoder.

3.   Knyt objektet til det JTree-objekt, der skal vises.

4.   Vis JTree-objektet.

 

DefaultTreeCellRenderer-objektet tilknyttes JTree-objektet ved hjælp af metoden setCellRenderer.

 

Nedenstående eksempel viser, hvordan man kan ændre udseendet af et træ. Der er taget udgangspunkt i klassen Indholdsfortegnelse fra kapitel 2.

 

// Opret træet

 

JTree tree = new JTree (bog);

 

// Indlæs ikoner

 

ImageIcon open = new ImageIcon (“open.gif”);

ImageIcon closed = new ImageIcon (“closed.gif”);

ImageIcon leaf = new ImageIcon (“leaf.gif”);

 

 

// Tilpas træet

 

DefaultTreeCellRenderer cellRenderer = new DefaultTreeCellRenderer();

cellRenderer.setClosedIcon (closed);

cellRenderer.setOpenIcon (open);

cellRenderer.setLeafIcon (leaf);

 

tree.setCellRenderer (cellRenderer);

 

// Tilføj træet til vinduet

 

getContentPane().add (tree);

Figur 3.5 viser, hvordan indholdsfortegnelsen ser ud med de nye ikoner.

 

 

Fig. 3.5 – Et træ med brugerdefinerede ikoner.

 

 

 

 

 

 

 

Værktøjslinier

 

Værktøjslinier er ikke blevet behandlet tidligere. Det skyldes, at repræsentationen af dem udelukkende er grafisk. I Java bliver værktøjslinier repræsenteret af klassen JToolBar, der har følgende signatur:

 

public class JToolBar extends com.sun.java.swing.JComponent implements com.sun.java.swing.SwingConstants, com.sun.java.accessibility.Accessible

{

public com.sun.java.swing.JToolBar();

public com.sun.java.swing.JToolBar(int);

public com.sun.java.swing.JButton add(com.sun.java.swing.Action);

public void addSeparator();

public void addSeparator(java.awt.Dimension);

public int getOrientation();

public boolean isBorderPainted();

public boolean isFloatable();

public void setBorderPainted(boolean);

 

public void setFloatable(boolean);

public void setMargin(java.awt.Insets);

public void setOrientation(int);

}

 

Som det ses af ovenstående signatur, kan værktøjslinien enten være lodret eller vandret. Dette angives ved hjælp af setOrientation-metoden. Man angiver lodret eller vandret ved hjælp af henholdsvis JToolbar.VERTICAL og JToolbar.HORIZONTAL. Hvis man ikke angiver andet, kan brugeren trække værktøjslinien rundt i vinduet. Han kan endda trække den udenfor vinduet og få værktøjslinien vist i sit eget vindue. Denne mulighed kan slås fra ved at kalde setFloatable(false).

 

Ved hjælp af add-metoden kan man tilføje knapper eller Action-objekter til værktøjslinien. Både knapper og Action-objekter er beskrevet tidligere i denne bog. I denne forbindelse skal man blot være opmærksom på, at man skal huske at tilføje et ikon til begge dele, før de tilføjes til værktøjslinien.

 

Klassen ToolbarExample, der ses herunder, viser, hvordan man kan oprette en simpel værktøjslinie med to knapper:

 

import com.sun.java.swing.*;

import java.awt.*;

import java.awt.event.*;

 

public class ToolbarExample extends JFrame

{

public ToolbarExample ()

{

// Tilpas vinduet

 

super (“Værktøjslinie”);

setSize (400,200);

 

// Opret værktøjslinien

 

JToolBar toolbar = new JToolBar();

 

// Indlæs ikoner

 

ImageIcon openfile = new ImageIcon (“openfile.gif”);

ImageIcon savefile = new ImageIcon (“savefile.gif”);

 

// Opret knapper

 

JButton open = new JButton (openfile);

JButton save = new JButton (savefile);

 

// Tilføj knapper til værktøjslinie

 

toolbar.add (open);

toolbar.add (save);

 

 

// Tilføj værktøjslinien til vinduet

 

getContentPane().add (toolbar, BorderLayout.NORTH);

 

// Tilføj et tekstfelt til vinduet

 

JTextArea tekst = new JTextArea();

getContentPane().add (tekst);

 

// Tilføj eventhåndtering

 

open.setActionCommand (“open”);

save.setActionCommand (“save”);

 

ActionListener listener = new ToolbarListener(tekst);

open.addActionListener (listener);

save.addActionListener (listener);

}

 

public static final void main (String args[])

{

ToolbarExample me = new ToolbarExample();

me.show();

}

}

 

Klassen indlæser ikonerne ved hjælp af ImageIcon’s constructor. Herefter oprettes der to knapper med ikonerne. Disse tilføjes til værktøjslinien, der derefter tilføjes vinduet som ethvert andet komponent. Da værktøjslinien består af knapper, foretages værktøjsliniens eventhåndtering efter samme principper som ved almindelige knapper. Det fremgår af klassen ToolbarListener:

 

class ToolbarListener implements ActionListener

{

private JTextArea tekst;

 

public ToolbarListener (JTextArea tekst)

{

this.tekst = tekst;

}

 

public void actionPerformed (ActionEvent e)

{

if (e.getActionCommand().equals(“open”))

{

tekst.append (“Du valgte at åbne\n”);

}

else

if (e.getActionCommand().equals(“save”))

{

tekst.append (“Du valgt at gemme\n”);

}

 

}

}

 

Udførsel af programmet resulterer i det skærmbillede, der fremgår af figur 3.6.

 

 

Fig. 3.6 – En værktøjslinie.

 

 

Billedbehandling

 

At lave en pæn brugergrænseflade med Swing kræver en del grafik. Det kræver også flere forskellige udgaver af det samme billede. For eksempel skal man have et billede, der vises på en knap i al almindelighed. Man skal også have et billede, når knappen ikke kan vælges, et billede når musemarkøren bevæges henover knappen og så videre. Det er tidskrævende at skulle lave disse billeder manuelt, og det gør også de filer, der skal distribueres sammen med programmet, større. Det kan især have betydning, hvis programmet skal hentes over et netværk, før det kan udføres. Heldigvis stiller Java en række klasser og metoder til rådighed, der gør det muligt at lave forskellige versioner af et billede – disse metoder og klasser bliver gennemgået i dette afsnit.

 

Grayfilter

 

Klassen GrayFilter bruges til at lave de grå billeder, der bruges, når en knap er inaktiv. Klassen har en statisk funktion, der laver et almindeligt billede om til et gråtonet. Udover at lave billedet gråtonet, bliver billedets pixels gjort lysere. Denne funktion er GrayFilter.createDisabledImage. Eneste parameter til funktionen er det billede, der skal gråtones. Funktionen returnerer det gråtonede billede som et Image-objekt.

 

Nedenstående eksempel indlæser et billede og viser både det og det gråtonede billede.

 

import java.awt.swing.*;

import java.awt.*;

 

import com.sun.java.swing.*;

import java.awt.*;

 

public class GrayImage extends JFrame

{

private static GrayImage me;

 

 

private Image normalImage, grayImage;

private JLabel normalLabel, grayLabel;

 

public GrayImage()

{

// Tilpas vinduet

 

super (“Gråtonet billede”);

setSize(380,130);

getContentPane().setLayout(new GridLayout());

 

// Indlæs billedet som ikon

 

ImageIcon normal = new ImageIcon(“javalogo.gif”);

 

// Konverter ikonet til et billede

 

normalImage = normal.getImage();

 

// Konverter til gråtoner

 

grayImage = GrayFilter.createDisabledImage(normalImage);

 

// Opret labels med billederne

 

normalLabel = new JLabel(normal);

ImageIcon gray = new ImageIcon(grayImage);

grayLabel = new JLabel(gray);

 

// Tilføj labels til brugergrænsefladen

 

getContentPane().add(normalLabel);

getContentPane().add(grayLabel);

}

 

public static final void main(String args[])

{

me = new GrayImage();

me.show();

}

}

 

Klassen indlæser først billedet som et ikon. Herefter konverteres dette ikon til et billedet ved hjælp af getImage-funktionen. På baggrund af det returnerede billede oprettes det gråtonede billede. Dette billede konverteres igen til et ikon ved hjælp af ImageIcon’s constructor. De to ikoner bliver herefter sat på hver deres label, der bliver føjet til vinduet. Det giver et vindue, der svarer til figur 3.7.

 

 

 

Fig. 3.7 – Et almindeligt og et gråtonet billede.

 

 

Egne filtre

 

Hvis man vil bruge sine egne filtre til at håndtere billeder, er man nødt til at forstå den bagvedliggende teori.

 

I Java bruges en billedproducent – image producer – til at oprette de data, der udgør billedet. Disse sendes til en billedforbruger – image consumer. De to elementer repræsenteres af de to interfaces, ImageProducer og ImageConsumer. Disser har følgende signatur:

 

public abstract interface ImageProducer extends java.lang.Object

{

public abstract void addConsumer(java.awt.image.ImageConsumer);

public abstract boolean isConsumer(java.awt.image.ImageConsumer);

public abstract void removeConsumer(java.awt.image.ImageConsumer);

public abstract void requestTopDownLeftRightResend(java.awt.image.ImageConsumer);

public abstract void startProduction(java.awt.image.ImageConsumer);

}

 

public abstract interface ImageConsumer extends java.lang.Object

{

public abstract void imageComplete(int);

public abstract void setColorModel(java.awt.image.ColorModel);

public abstract void setDimensions(int, int);

public abstract void setHints(int);

public abstract void setPixels(int, int, int, int, java.awt.image.ColorModel, byte[], int, int);

public abstract void setPixels(int, int, int, int, java.awt.image.ColorModel, int[], int, int);

public abstract void setProperties(java.util.Hashtable);

}

 

Man har mulighed for at indsætte et billedfilter – image filter – mellem billedproducenten og billedforbrugeren. Billedfilteret ændrer de data, billedproducenten sender, så billedforbrugeren modtager de ændrede data og dermed et ændret billede.

 

Et billedfilter bliver repræsenteret af klassen ImageFilter, der har nedenstående signatur.

 

 

public class ImageFilter extends java.lang.Object implements java.awt.image.ImageConsumer, java.lang.Cloneable

{

public java.awt.image.ImageFilter();

public java.awt.image.ImageFilter getFilterInstance(java.awt.image.ImageConsumer);

public void imageComplete(int);

public void resendTopDownLeftRight(java.awt.image.ImageProducer);

public void setColorModel(java.awt.image.ColorModel);

public void setDimensions(int, int);

public void setHints(int);

public void setPixels(int, int, int, int, java.awt.image.ColorModel, byte[], int, int);

public void setPixels(int, int, int, int, java.awt.image.ColorModel, int[], int, int);

public void setProperties(java.util.Hashtable);

}

 

Hvis man selv vil oprette et billedfilter, skal man oprette en klasse, der nedarver fra ImageFilter.

 

Farvefiltre

 

Hvis det filter, man vil lave, skal foretage ændringer i et billedes farver eller transparens, er det nemmere at nedarve fra RGBImageFilter end at nedarve direkte fra ImageFilter.

 

RGBImageFilter har følgende signatur:

 

public abstract class RGBImageFilter extends java.awt.image.ImageFilter

{

public java.awt.image.RGBImageFilter();

public java.awt.image.IndexColorModel filterIndexColorModel(java.awt.image.IndexColorModel);

public abstract int filterRGB(int, int, int);

public void filterRGBPixels(int, int, int, int, int[], int, int);

public void setColorModel(java.awt.image.ColorModel);

public void setPixels(int, int, int, int, java.awt.image.ColorModel, byte[], int, int);

public void setPixels(int, int, int, int, java.awt.image.ColorModel, int[], int, int);

public void substituteColorModel(java.awt.image.ColorModel, java.awt.image.ColorModel);

}

 

For at oprette et billedfilter, der nedarver fra RGBFilterImage, behøver man kun at implementere metoden filterRGB. Denne metode konverterer en enkelt pixel. Klassen ColorFilter viser, hvordan man kan lave et billedfilter, der konverterer farverne i et billede baseret på en farveværdi, der sendes med til filterets constructor.

 

 

import java.awt.image.*;

 

public class ColorFilter extends RGBImageFilter

{

private int redValue;

private int greenValue;

private int blueValue;

 

public ColorFilter(int rgb)

{

canFilterIndexColorModel = true;

 

redValue = (rgb >> 16) & 0xff;

greenValue = (rgb >> 8) & 0xff;

blueValue = (rgb >> 0) & 0xff;

}

 

public int filterRGB (int x, int y, int rgb)

{

int r = (rgb >> 16) & 0xff;

int g = (rgb >> 8) & 0xff;

int b = (rgb >> 0) & 0xff;

 

int redPercent = redValue * 100 / 255;

int greenPercent = greenValue * 100 / 255;

int bluePercent = blueValue * 100 / 255;

 

r = (255 – ((255 – r) * (100 – redPercent) / 100));

g = (255 – ((255 – g) * (100 – greenPercent) / 100));

b = (255 – ((255 – b) * (100 – bluePercent) / 100));

 

if (r < 0) r = 0;

if (r > 255) r = 255;

if (g < 0) g = 0;

if (g > 255) g = 255;

if (b < 0) b = 0;

if (b > 255) b = 255;

return (rgb & 0xff000000) | (r << 16) | (g << 8) | (b << 0);

}

}

 

Selve konverteringen af farverne er ren matematik. Det samme er konverteringen fra en int til tre int, hver indeholdende mængden af en af grundfarverne.

 

For at anvende et billedfilter i sin applikation skal man gennemgå fem punkter:

 

1.   Opret et Image-objekt.

2.   Brug Image-objektets getSource-metode til at få en reference til billedes producent.

3.   Opret en instans af filteret.

 

 

4.   Opret et FilteredImageSource-objekt. Billedproducenten og -filteret er parametre til constructoren.

5.   Brug createImage-metoden til at oprette et nyt billede, der bruger FilteredImageSource-objektet som producent.

 

Umiddelbart lyder det kompliceret, men som nedenstående eksempel viser, er det faktisk nemt at implementere.

 

Klassen FilterExample bruger ColorFilter til at lade brugeren ændre på udseendet af et billede. Brugeren har ved hjælp af skydere mulighed for at indstille mængde af rød, grøn og blå. FilterImage er implementeret således:

 

import com.sun.java.swing.*;

import com.sun.java.swing.event.*;

import java.awt.*;

import java.awt.image.*;

 

public class FilterExample extends JFrame implements ChangeListener

{

private JSlider redSlider, greenSlider, blueSlider;

private Image normalImage;

private JLabel alm, clr;

 

public FilterExample ()

{

// Tilpas vinduet

 

super (“Farvedemo”);

setSize (300,400);

getContentPane().setLayout (new GridLayout(2,1));

 

// Opret komponenter til redigering af farver

 

JPanel redPanel = new JPanel();

redPanel.setLayout (new GridLayout(2,1));

redSlider = new JSlider (JSlider.HORIZONTAL, 0, 255, 0);

JLabel redLabel = new JLabel (“Rød”);

redPanel.add (redLabel);

redPanel.add (redSlider);

 

JPanel greenPanel = new JPanel();

greenPanel.setLayout (new GridLayout(2,1));

greenSlider = new JSlider (JSlider.HORIZONTAL, 0, 255, 0);

JLabel greenLabel = new JLabel (“Grøn”);

greenPanel.add (greenLabel);

greenPanel.add (greenSlider);

 

JPanel bluePanel = new JPanel();

bluePanel.setLayout (new GridLayout(2,1));

blueSlider = new JSlider (JSlider.HORIZONTAL, 0, 255, 0);

 

JLabel blueLabel = new JLabel (“Blå”);

bluePanel.add (blueLabel);

bluePanel.add (blueSlider);

 

// Opret to billeder

 

ImageIcon logo = new ImageIcon (“javalogo.gif”);

normalImage = logo.getImage();

 

alm = new JLabel (logo);

clr = new JLabel (logo);

 

JPanel billedPanel = new JPanel();

billedPanel.setLayout (new GridLayout(2,2));

JLabel before = new JLabel (“Før”);

JLabel after = new JLabel (“Efter”);

billedPanel.add (before);

billedPanel.add (after);

billedPanel.add (alm);

billedPanel.add (clr);

 

// Tilføj komponenter til brugergrænsefladen

 

JPanel sliderPanel = new JPanel();

sliderPanel.add (redPanel);

sliderPanel.add (greenPanel);

sliderPanel.add (bluePanel);

 

getContentPane().add(billedPanel);

getContentPane().add(sliderPanel);

 

// Tilføj eventhåndterng

 

redSlider.addChangeListener (this);

greenSlider.addChangeListener (this);

blueSlider.addChangeListener (this);

}

 

public static final void main (String args[])

{

FilterExample me = new FilterExample();

me.show();

}

 

public void stateChanged (ChangeEvent e)

{

ImageProducer producer = normalImage.getSource();

 

int r,g,b;

 

r = redSlider.getValue();

g = greenSlider.getValue();

b = blueSlider.getValue();

 

 

ColorFilter filter = new ColorFilter (r << 16 | g << 8 | b);

FilteredImageSource filtered = new FilteredImageSource (producer, filter);

Image newImage = createImage (filtered);

clr.setIcon (new ImageIcon (newImage));

}

 

}

 

Det er metoden stateChanged, der gør brug af filteret. Først fås en reference til en billedproducent ved at kalde getSource på det originale billede. Dernæst oprettes filteret på baggrund af værdien af de skydere, brugeren angiver farven med. Derefter oprettes et FilteredImageSource-objekt. Her sendes produceren og filteret med som parametre til constructoren. Med udgangspunkt i det oprettede objekt opretter createImage det nye billede. Dette billede placeres på den label, der viser det nye billede. Alt i alt ender det med et skærmbillede som det, der ses af figur 3.8.

 

 

Fig. 3.8 – Anvendelse af et billedfilter.

 

Som det ses af signaturen af metoden filterRGB i filterklassen, får filteret også oplysninger om x- og y-positionen af den pixel, der skal konverteres. Det betyder, at man kan lave et filter, der for eksempel toner billederne. GradientFilter er et sådant filter. Jo længere mod højre en pixel befinder sig, jo lysere bliver den, når den har været igennem filteret.

 

Nedenstående program viser implementeringen af GradientFilter.

 

import java.awt.image.*;

 

public class GradientFilter extends RGBImageFilter

 

{

private int imageWidth;

 

public GradientFilter (int imageWidth)

{

this.imageWidth = imageWidth – 1;

}

 

public int filterRGB (int x, int y, int rgb)

{

int r = (rgb >> 16) & 0xff;

int g = (rgb >> 8) & 0xff;

int b = (rgb >> 0) & 0xff;

 

int level = x * 100 / imageWidth;

 

r = (255 – ((255 – r) * (100 – level) / 100));

g = (255 – ((255 – g) * (100 – level) / 100));

b = (255 – ((255 – b) * (100 – level) / 100));

 

if (r < 0) r = 0;

if (r > 255) r = 255;

if (g < 0) g = 0;

if (g > 255) g = 255;

if (b < 0) b = 0;

if (b > 255) b = 255;

return (rgb & 0xff000000) | (r << 16) | (g << 8) | (b << 0);

}

}

 

Variablen level beregnes på baggrund af x-værdien. Jo højere x-værdi, jo højere level. Værdien af level bliver brugt i beregningen af de nye farveværdier, og en høj level-værdi vil give en lys farve. Derfor bliver billedet lysere og lysere mod højre.

 

Klassen GradientExample er et kort eksempel på, hvordan GradientFilter-klassen kan benyttes.

 

import com.sun.java.swing.*;

import java.awt.*;

import java.awt.image.*;

 

public class GradientExample extends JFrame

{

public GradientExample()

{

// Tilpas vinduet

 

super (“Gradient”);

setSize (300,200);

getContentPane().setLayout(new GridLayout (1,2));

 

 

// Indlæs billedet

 

ImageIcon logo = new ImageIcon (“javalogo.gif”);

 

// Konverter det ved hjælp af filteret

 

Image img = logo.getImage();

ImageProducer producer = img.getSource();

GradientFilter filter = new GradientFilter (img.getWidth(this));

FilteredImageSource filtered = new FilteredImageSource (producer, filter);

Image gradientImage = createImage (filtered);

 

// Opret to labels med billederne

 

getContentPane().add(new JLabel(logo));

getContentPane().add(new JLabel(new ImageIcon (gradientImage)));

}

 

public static final void main (String args[])

{

GradientExample me = new GradientExample();

me.show();

}

}

 

Igen ses det, at programmet består af de fem punkter, der blev gennemgået tidligere. Først oprettes billedet, dernæst opnås en reference til billedets producent. Derefter oprettes filteret, og der oprettes et FilteredImageSource-objekt, som indeholder det konverterede billede. På baggrund af dette objekt oprettes det endelige billede, der kan føjes til brugergrænsefladen.

 

Avancerede filtre

 

Man kan imidlertid lave mere avancerede filtre end filtre, der ændrer farven på de forskelige pixels. Faktisk kan man fuldstændigt ændre og flytte alle pixels i et billede.

 

Hvis man vil lave et avanceret filter, skal man lade sin filterklasse nedarve direkte fra ImageFilter. Her skal man som minimum overskrive den ene af de to setPixels-metoder – de fleste filtre overskriver dem begge. Semantikken i parameterlisten til de to funktioner er den samme.

 

1.   Den første x-værdi.

2.   Den første y-værdi.

3.   Bredden af det rektangel, der skal konverteres.

4.   Højden af det rektangel, der skal konverteres.

5.   En reference til en color model.

6.   En reference til et array indeholdende billedets pixels. Dette kan enten være i form af int-værdier eller byte-værdier.

 

 

7.   Offset i pixels.

8.   Liniernes længde i pixels.

 

Klassen FlipFilter vender et billede på hovedet. Dette sker netop ved at overskrive setPixels-metoderne.

 

import java.awt.image.*;

 

public class FlipFilter extends ImageFilter

{

public void setPixels(int x, int y, int w, int h, ColorModel model, int pixels[], int off, int scansize)

{

int arraySize = pixels.length;

 

int buffer[] = new int [arraySize];

for (int i = 0; i < arraySize; i++)

{

buffer[arraySize – 1 – i] = pixels[i];

}

 

consumer.setPixels (x, y, w, h, model, buffer, off, scansize);

consumer.imageComplete(consumer.STATICIMAGEDONE);

}

 

public void setPixels(int x, int y, int w, int h, ColorModel model, byte pixels[], int off, int scansize)

{

int arraySize = pixels.length;

 

byte buffer[] = new byte [arraySize];

for (int i = 0; i < arraySize; i++)

{

buffer[arraySize – 1 – i] = pixels[i];

}

consumer.setPixels (x, y, w, h, model, buffer, off, scansize);

consumer.imageComplete(consumer.STATICIMAGEDONE);

}

 

}

 

Selve konverteringen er der ikke mange ben i. Der oprettes en buffer, som kan indeholde det nye billede. Herefter er det bare at lægge den første pixel bagerst, den næste næstsidst og så videre. Det interessante kommer, når konverteringen er færdig. Så skal det nye billede sendes videre til den forbruger, billedet oprindeligt var bestemt for. Det gør man ved at kalde setPixels-metoden på consumer-objektet. Da filteret nu er færdigt med at behandle billedet, kaldes også imageComplete-metoden.

 

Bemærk, at dette eksempel ikke tager højde for, at der kan opstå fejl i forbindelse med konverteringen og transport af data fra producent til filter til forbruger. Hvis man vil implementere fejlkontrol i sit filter, skal man

 

implementere flere funktioner. Det drejer sig blandt andet om imageComplete-metoden.

 

Figur 3.9 viser resultatet af flipfilteret udført på Java-logoet.

 

 

Fig. 3.9 – Resultatet af flipfilteret.

 

Animationer

 

Når det er så let at tilføje ikoner til forskellige Swing-komponenter, kunne man godt ønske sig at have en komponent, der kunne indeholde en gif-animation. Java understøtter endnu ikke gif-animationer, så man er nødt til at klare sig med en række af enkeltstående gif-billeder.

 

I forbindelse med udvikling af en sådan komponent skal man være opmærksom på de regler, der blev beskrevet i kapitel 2 med hensyn til Swing og tråde.

 

Et animationskomponent skal arbejde på en række af billeder eller navne på filer, der indeholder billeder. Når komponenten vises, skal billederne vises i rækkefølge i et givent interval.

 

Krav til animationskomponenten kunne være

 

  • at klassen skal nedarve fra Component, så den kan tilføjes til vinduer og lignende.
  • at brugeren af komponenten selv kan fastsætte intervallet – det vil sige animationens hastighed.
  • at brugeren kan vælge at stoppe og starte animationen.

 

Disse krav opfyldes alle af klassen AnimatedLabel. Denne klasse nedarver fra JLabel, hvilket giver klassen nem adgang til at vise ikoner, ligesom det gør, at klassen indirekte nedarver fra Component.

 

Implementeringen af AnimatedLabel-klassen ses herunder.

 

import com.sun.java.swing.*;

import java.awt.event.*;

 

public class AnimatedLabel extends JLabel implements ActionListener

{

private ImageIcon images[];

private int currentImage;

 

private int interval = 250;

 

private int direction = 1;

 

private Timer timer;

 

public AnimatedLabel (String files[])

{

super (new ImageIcon(files[0]));

 

images = new ImageIcon[files.length];

 

for (int i = 0; i < files.length; i++)

{

images[i] = new ImageIcon (files[i]);

}

}

 

public AnimatedLabel (String files[], int interval)

{

this (files);

this.interval = interval;

}

 

public AnimatedLabel (ImageIcon images[])

{

super(images[0]);

this.images = images;

}

 

public AnimatedLabel (ImageIcon images[], int interval)

{

this(images);

this.interval = interval;

}

 

public void setDelay (int interval)

{

this.interval = interval;

timer.setDelay (interval);

}

 

public int getDelay ()

{

return interval;

}

 

public void reset()

{

currentImage = -1;

}

 

public void start()

{

if (timer == null)

 

{

timer = new Timer(interval, this);

reset();

}

timer.setInitialDelay (0);

timer.start();

}

 

public void stop()

{

if (timer != null)

{

timer.stop();

}

}

 

public void actionPerformed(ActionEvent e)

{

if (e.getSource() == timer)

{

currentImage += direction;

if (currentImage >= images.length || currentImage < 0)

{

direction = -direction;

currentImage += direction;

}

 

super.setIcon(images[currentImage]);

}

}

}

 

Klassen har fire constructors. De to af dem tager henholdsvis et array af strenge indeholdende filnavne og et array af ImageIcon-objekter. De to andre tager et af de to typer arrays sammen med en int, der angiver intervallet mellem visning af billeder. Intervallet angives i millisekunder.

 

Hver af constructorne kalder superklassens constructor med en reference til det første billede i animationen. Da JLabel har en constructor, der tager et Icon-objekt som parameter, betyder dette, at det første billede i animationen vil blive vist, indtil animationen startes.

 

Klassen indeholder to funktioner, der styrer animationen. Det er start og stop, der henholdsvis starter og stopper den timer, som kalder animationen. Grunden, til at der bruges en timer til at styre animationen, skal findes i de regler om tråde og Swing, der blev gennemgået i kapitel 2.

 

Hvis Timer-objektet ikke allerede er instantieret, når start-metoden kaldes, bliver det det. Den initiale forsinkelse sættes til 0, så animationen starter, så snart start-metoden udføres. Hvis start-metoden kaldes efter stop-metoden, vil animationen fortsætte, hvor den blev afbrudt. Ønsker man, at animationen skal starte forfra, skal man bruge reset-metoden.

 

Til at ændre animationshastigheden findes metoden setDelay. Når denne kaldes ændres intervallet i klassen, og intervallet på timeren ændres også.

 

Det betyder, at en ændring i intervallet omgående vil afspejles i animationshastigheden, hvis animationen er startet.

 

Hver gang timerens interval er gået, opstår en action-event, der håndteres af metoden actionPerformed. Denne metode sørger for, at det næste billede i animationen bliver vist. Animationen spilles først forlæns og så baglæns. Derfor findes variablen direction til at angive, hvilken retning animationen kører i i øjeblikket. Er direction positiv (+1) kører animationen fremad, mens en negativ værdi (-1) angiver, at animationen kører baglæns. Når animationen når til det sidste eller det første billede, vendes retningen.

 

For at opdatere det billede, der vises, kaldes superklassens setIcon-metode.

 

Formålet med klassen var, at den skulle kunne anvendes ligesom alle andre komponentklasser. Som det fremgår af nedenstående eksempel, er det også tilfældet.

 

import com.sun.java.swing.*;

import com.sun.java.swing.event.*;

import java.awt.*;

import java.awt.event.*;

 

public class Animation extends JFrame

{

public Animation()

{

// Tilpas vinduet

 

super (“Animation”);

setSize(300,250);

getContentPane().setLayout(new GridLayout(3,1));

 

// Opret liste over filnavne

 

String files[] = new String[32];

 

for (int i = 0; i < 32; i++)

{

files[i] = new String (“duketest” + (i < 10 ? “0” : “”) + i + “.gif”);

}

 

// Opret AnimatedLabel-objektet

 

AnimatedLabel label = new AnimatedLabel (files);

 

// Tilføj objektet til vinduet

 

getContentPane().add(label);

 

// Opret komponenter til styring af animationen

 

JSlider hastighed = new JSlider(JSlider.HORIZONTAL, 0, 1000, 200);

hastighed.setMajorTickSpacing(200);

 

hastighed.setMinorTickSpacing(50);

hastighed.setPaintTicks(true);

getContentPane().add(hastighed);

 

JButton start = new JButton (“Start”);

JButton stop = new JButton (“Stop”);

JPanel panel = new JPanel();

panel.setLayout (new GridLayout(1,2));

panel.add(start);

panel.add(stop);

getContentPane().add(panel);

 

// Tilføj eventhåndtering

 

hastighed.addChangeListener (new HastighedListener(label));

 

start.setActionCommand (“start”);

stop.setActionCommand (“stop”);

 

ActionListener listener = new KnapListener (label);

start.addActionListener (listener);

stop.addActionListener (listener);

}

 

public static final void main (String args[])

{

Animation me = new Animation();

me.show();

}

}

 

Klassens constructor opretter først et array af tekststrenge, der indeholder navnene på de billedfiler, der skal indlæses. I dette tilfælde drejer det sig om filnavnene animation00.gif – animation31.gif.

 

Derefter oprettes et AnimatedLabel-objekt på baggrund af array’et, og dette føjes til vinduet. Resten af constructoren sørger for at oprette en skyder, der bruges til at indstille hastigheden af animationen, og knapper til at starte og stoppe animationen.

 

De events, skyderen genererer, bliver håndteret af klassen HastighedListener. Denne er implementeret således:

 

class HastighedListener implements ChangeListener

{

private AnimatedLabel label;

 

public HastighedListener (AnimatedLabel label)

{

this.label = label;

}

 

public void stateChanged (ChangeEvent e)

{

label.setDelay (((JSlider) e.getSource()).getValue());

 

}

}

 

Metoden stateChanged sørger for, at animationens hastighed afspejles af skyderen. Bemærk at skyderen ikke viser hastighed men forsinkelse. Det vil sige, at jo længere skyderen flyttes mod højre, jo langsommere vil animationen køre.

 

Håndtering af de events, der opstår, når man trykker på en af de to knapper, foretages i klassen KnapListener:

 

class KnapListener implements ActionListener

{

private AnimatedLabel label;

 

public KnapListener (AnimatedLabel label)

{

this.label = label;

}

 

public void actionPerformed (ActionEvent e)

{

if (e.getActionCommand().equals(“start”))

{

label.start();

}

else

if (e.getActionCommand().equals(“stop”))

{

label.stop();

}

}

}

KnapListener-klassen kigger på den indkommende events action-command, hvorefter den enten starter eller stopper animationen. Animationen vil blive startet, hvor den blev stoppet. Det vil sige, at eksemplet ikke gør brug af AnimatedLabel’s reset-metode.

Figur 3.10 viser, hvordan eksemplet ser ud.

Fig. 3.10 – Animation.

 

 

 

 

 

 

 

Optimering af animation

 

Store animationer bruger typisk en stor mængde billeder. Disse tager lang tid at hente over et netværk – ikke bare på grund af filernes størrelse, men også på grund af det spild, der er ved at bruge http-protokollen. Noget af dette spild kan undgås ved at samle filerne i én fil – det kan man eksempelvis gøre ved at samle det hele i en jar-fil.

 

Man kan også vælge at samle hele animationen i én billedfil. Det ser man for eksempel i gif-animationer. Man kan samle animationen ved at placere de forskellige billeder i animationen ved siden af hinanden. Figur 3.11 viser, hvordan sådan et billede kan komme til at se ud.

 

 

 

Fig. 3.11 – En del af en animation placeret i én fil.

 

Når man skal vise billedet, skal man først markere, at man kun vil vise en del af billedet. Det gør man ved hjælp af metoden clipRect. Herefter skal man flytte billedet til venstre, så kun den del af billedet, der skal vises, befinder sig i det område, man angav med clipRect. Nedenstående klasse viser, hvordan man kan lave en animation.

 

import com.sun.java.swing.*;

import java.awt.*;

import java.awt.event.*;

 

public class StripAnimation extends JFrame implements ActionListener

{

private Image images;

private int imageNo;

private final int imageCount;

private final int imageWidth;

private final int imageHeight;

private int direction;

 

public StripAnimation()

{

// Tilpas vinduet

super (“Animation”);

setSize(100,100);

 

// Indlæs billeder og foretag beregninger

MediaTracker tracker = new MediaTracker (this);

images = Toolkit.getDefaultToolkit().getImage (“animationStrip.gif”);

tracker.addImage (images, 0);

 

 

try

{

tracker.waitForAll();

}

catch (InterruptedException ie)

{

}

 

imageNo = 0;

imageCount = 32;

 

imageWidth = images.getWidth(this) / imageCount;

imageHeight = images.getHeight(this);

 

direction = 1;

 

// Indstil en timer

 

Timer timer = new Timer(200, this);

timer.start();

}

 

public void paint (Graphics g)

{

// Ryd baggrunden

g.setColor(getBackground());

g.fillRect(20, 30, imageWidth, imageHeight);

g.setColor(Color.black);

 

// Tegn det nye billede

 

g.clipRect (20,30, imageWidth, imageHeight);

g.drawImage (images, 20-(imageNo * imageWidth), 30, this);

}

 

public void actionPerformed (ActionEvent e)

{

imageNo += direction;

if (imageNo >= imageCount || imageNo < 0)

{

direction = -direction;

}

repaint();

}

 

public static final void main (String args[])

{

StripAnimation me = new StripAnimation();

me.show();

}

}

 

Programmet starter med at indlæse billedfilen gennem Toolkit’s getImage-metode. Dernæst beregnes bredden af hvert enkelt billede baseret på bredden af det samlede billede og antallet af små billeder. Et Timer-objekt tilsluttes for at sikre regelmæssig opdateringen af billedet. Hver gang timerens interval er gået, spoler actionPerformed frem til det næste billede og kalder repaint, der sørger for opdateringen. Ligesom i AnimatedLabel bruges direction til at angive, om animationen bliver vist forlæns eller baglæns.

 

I paint-metoden skal man være opmærksom på, at man selv er nødt til at rense skærmen, før man tegner det næste billede. I dette eksempel bliver animationen tegnet på positionen (20,30). Derfor sættes det aktive område til at starte i (20,30) og til at have billedets bredde og højde. Ligeledes lægges henholdsvis 20 og 30 til x- og y-koordinaterne, når billedet tegnes.

 

X-koordinaten i kaldet til drawImage opstår som det negerede produkt af billedets position i animationen og billedets bredde. Dette skyldes, at det store billede skal flyttes, så den del af billedet, der skal vises, passer med det område, der blev angivet i clipRect.

 

Det skal bemærkes, at eksemplet blot viser, hvordan opbygningen af en animation kan være. I en virkelig applikation skal man naturligvis gøre brug af double-buffering for at undgå, at animationen blinker. Ved double-buffering tegner man først det nye billede på et usynligt Graphics, der siden vises på skærmen. Nedenstående eksempel viser, hvordan StripAnimation’s paint-metode kommer til at se ud, hvis double-buffering anvendes:

 

public void paint (Graphics g)

{

// Doublebuffering

 

Dimension d = size();

Image offImage = createImage(d.width, d.height);

Graphics buffer = offImage.getGraphics();

 

// Ryd baggrunden

buffer.setColor(getBackground());

buffer.fillRect(20, 30, imageWidth, imageHeight);

buffer.setColor(Color.black);

 

// Tegn det nye billede

 

buffer.clipRect (20,30, imageWidth, imageHeight);

buffer.drawImage (images, 20-(imageNo * imageWidth), 30, this);

 

// Kopier bufferen

g.drawImage (offImage, 0, 0, this);

 

}

 

Afrunding

 

Gennemgangen af Swing er færdig. I dette kapitel er det blevet beskrevet, hvordan man ved hjælp af ImageIcon-klassen kan tilføje billeder til de fleste af Swings komponenter. Kapitler har også omhandlet, hvordan man kan bruger timers til at lave en animation som komponent eller ved hjælp af paint-metoden.

 

FAQ

 

Kan man kun indlæse billeder gennem ImageIcon-objekter?

 

Nej. Man kan også vælge at bruge en af de statiske metoder i Toolkit-klassen. Det drejer sig om metoderne

 

public abstract java.awt.Image getImage (java.lang.String filename);

og

public abstract java.awt.Image getImage (java.net.URL);

Det vil sige, at man kan hente billeder ved hjælp af nedenstående programstump.

Toolkit toolkit = Toolkit.getDefaultToolkit();

Image billede = toolkit.getImage(“billede.gif”);

eller

Toolkit toolkit = Toolkit.getDefaultToolkit();

Image billede = toolkit.getImage(new URL (“http://www.mitdomaene.dk/billede.gif”));

Hvordan indlæser man billeder fra appletter?

Appletter er alle instanser af klassen Applet. Denne klasse har to metoder til at hente billeder. Det drejer sig om

public Image getImage(URL url)

og

public Image getImage(URL url, String name)

Det er kun appletter, der kan bruge disse metoder. De kræver også, at der eksisterer en komplet appletkontekst. Det betyder, at de ikke kan kaldes i en constructor eller i et udtryk, der erklærer en statisk variabel. Metoderne skal i stedet bruges fra applettens init-metode.

 

Hvilke grafikformater understøttes af Java og Swing?

 

I øjeblikket kan man kun indlæse billeder i gif- og jpg-format. Man kan imidlertid sagtens implementere sit eget grafikformat. Man behøver blot at lave en klasse, der nedarver fra Image. Denne klasses constructor skal indlæse billedet fra en fil i det pågældende format. Dernæst skal man overskrive Image-klassens metoder, så man får konverteret det specielle grafikformat til et format, Java kan forstå.

 

 

De ikoner, jeg tilføjer til menuerne, ser sære ud. Hvad gør jeg forkert?

 

Hvis man tilføjer ikoner, der ikke har en transparent baggrund, vil baggrunden også blive vist på menuen. Det betyder, at menuen har en forkert baggrundsfarve de steder, hvor der er ikoner. Løsningen på dette problem er at hente ikonerne ind i et billedbehandlingsprogram og give dem en transparent baggrund.

Du læser en gammel bog

Den bog, du læser her, er fra 1998, og mange ting kan have ændret sig siden da.
Vi håber, at du stadig kan finde relevant information i den.
Hvis du vil læse aktuelle oplysninger om de avancerede dele af Java, anbefaler vi
bogen Core Java – Advanced Features

Comments are closed.