Graafikakomponendi loomine Omatehtud jooniste ning komponentide aluseks sobib lõuend (Canvas). Ta sobib lihtsaks joonistamiseks, kuid samas saab ta panna ka teateid vastu võtma, s.t. näiteks hiirevajutusele reageerima. Kui oled kord komponendi loonud, siis saad seda tervikuna kasutada seal kus parajasti vaja on. Kui oled komponendi tööga rahul, siis võid tarvitada teda ilma sisemisse ehitusse süvenemata. Alati ei pea komponendi loomisel kõike otsast tegema, vaid võib kasutada juba varem olemas olevaid tükke. Samuti võib luua varemvalmistatud komponendile alamklassi ning seal soovitud meetodid muuta. Nii võib kerge vaevaga lisada tekstialale võimaluse, et ta väljastaks oma sees oleva ridade arvu või paneks lisatavatele ridadele tühikud ette. Kui aga tahetakse sündmused ja kujundused täiesti ise määrata, siis tuleb aluseks võtta tühi pind ehk lõuend. Siin näites joonistatakse lõuendile soovitud nurkade arvuga hulknurk. Nurkade arvu saavad väljapoolsed muuta vaid meetodi abil. Iga muutmisega kaasneb uus joonistamine. import java.awt.*; public class Nurgad extends Canvas{ protected int nurkadearv; public Nurgad(){ nurkadearv=3; } public Nurgad(int uusarv){ nurkadearv=uusarv; } public void muudaNurkadeArv(int uusarv){ nurkadearv=uusarv; repaint(); } public void paint(Graphics g){ int korgus=getSize().height; int laius=getSize().width; double nurgavahe=2*Math.PI/(double)nurkadearv; int raadius=Math.min(korgus, laius)/3; int keskx=laius/2; int kesky=korgus/2; int vanax=keskx; int vanay=kesky+raadius; int uusx, uusy; for(int i=1; i<=nurkadearv; i++){ uusx=keskx+(int)(raadius*Math.sin(i*nurgavahe)); uusy=kesky+(int)(raadius*Math.cos(i*nurgavahe)); g.drawLine(vanax, vanay, uusx, uusy); vanax=uusx; vanay=uusy; } } } Loodud komponenti saab kasutada seal, kus vaja hulknurki joonistada. Siin näites pannakse rakendi ekraanile üheksa hulknurka, nurkade arvuga kolmest üheteistkümneni. import java.applet.Applet; public class Nurgarakend extends Applet{ public void init(){ setLayout(new java.awt.GridLayout(3, 3)); for(int nr=3; nr<12; nr++){ add(new Nurgad(nr)); } } } Samuti saab loodud komponendi abil lasta kasutajal valida, mitme nurgaga hulknurka soovib. Selleks panin rakendile kerimisriba ning loodud komponendi. Rakendi panin kerimisriba kuulajaks (AdjustmentListener). Kui kerimisriba määratud koha väärtust muudetakse, siis saadetakse uus väärtus rakendile, kes selle omakorda saadab hulknurka joonistavale komponendile. import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class Nurgarakend2 extends Applet implements AdjustmentListener{ Nurgad ng=new Nurgad(); Scrollbar sb=new Scrollbar( Scrollbar.VERTICAL, 3, 2, 2, 20 ); public void init(){ setLayout(new BorderLayout()); add("West", sb); add("Center", ng); sb.addAdjustmentListener(this); } public void adjustmentValueChanged(AdjustmentEvent e){ ng.muudaNurkadeArv(e.getValue()); } } Graphics2D Põhilised joonistamisfunktsioonid on klassis java.awt.Graphics. Alates versioonist 1.2 pakub laiendatud joonistamisvõimalusi eelmise alamklass Graphics2D. Kui esimesel juhul tuli alati arvutada ekraanikoordinaatides ning joone laiuseks oli üks punkt, siis siin võib valida omale sobiva taustsüsteemi ning ka joonistamisel saab enam parameetreid määrata. Kuna Graphics2D on klassi Graphics alamklass, siis ta oskab kõike mida eellanegi ning vajadusel saab temaga joonistada samade meetoditega, mis klassist Graphics omale sisse harjunud. Ühilduvuse huvides on komponendi meetodi paint parameetriks endiselt Graphics, kuid tegelikult antakse sellesse meetodisse joonistamiseks isend, kes suudab ka Graphics2D klassis kirjeldatud operatsioone täita. Juhul, kui spetsiifilisi omadusi vajatakse, tuleb muutuja tüüp enne teisendada. Joone omadused Joone tõmbamisel saab Graphics2D juures klassi BasicStroke abil määrata joone laiust, otsa kuju ning kahe joone ühendust (võimalused leiad API dokumetatsioonist). import java.awt.*; public class Joon1 extends Canvas{ public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; float laius=15; int jooneots=BasicStroke.CAP_ROUND; int uhendus=BasicStroke.JOIN_BEVEL; g.setStroke(new BasicStroke(laius, jooneots, uhendus)); g.drawRect(10, 10, 70, 100); g.drawLine(100, 10, 140, 200); } } Joonistusala piiramine Joonistamisala on võimalik piirata käsuga clip, andes ette kujundi, mille piires tohib joonistada. Siin näites määratakse joonistamise alaks ellipsi pind ning seejärel joonestatakse seest täidetud ristkülik. Tulemusena tekib ekraanile vaid ellipsi ning ristküliku ühisosa. import java.awt.*; import java.awt.geom.*; public class Kujund1 extends Canvas{ public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; Shape kujund=new Ellipse2D.Float(10, 10, 300, 100); g.clip(kujund); g.fillRect(20, 20, 400, 60); } } Venitamine, keeramine Joonistuspinda saab liigutada, venitada ja keerata. AffineTransform'i abil saab määrata, kuidas ja kui palju. Esimese näite puhul lükatakse koordinaatide alguspunkti saja ühiku võrra paremale ning alla. import java.awt.*; import java.awt.geom.AffineTransform; public class Transform2 extends Canvas{ public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; g.setTransform(AffineTransform.getTranslateInstance(100, 100)); g.fillRect(20, 20, 40, 20); } } rida g.setTransform(AffineTransform.getScaleInstance(3, 1.5)); suurendab joonist x-telje suunas 3 ning y-suunas 1,5 korda. g.setTransform(AffineTransform.getRotateInstance(Math.PI/4, 100, 75)); keerab joonist Pi/4 ehk 45 kraadi võrra ümber punkti 100, 75 g.setTransform(AffineTransform.getShearInstance(Math.PI/4, 0)); keerab püsttelge Pi/4 võrra. Kui soovida mitut muutust üheskoos, siis võib luua AffineTransform tüüpi isendi ning talle soovitud muutusi järjekorras rakendada. import java.awt.*; import java.awt.geom.AffineTransform; public class Transform6 extends Canvas{ public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; AffineTransform tr=new AffineTransform(); tr.rotate(Math.PI/4, 50, 100); tr.translate(150, 0); g.setTransform(tr); g.fillRect(20, 20, 40, 20); } } Muster, värviüleminek Joonistamisel saab lisaks ühele värvile soovi korral pinda katta ka nii värviülemineku kui piltidest koostatud mustriga. Soovitud katmisstiil tuleb määrata Graphics2D meetodiga setPaint. Värviülemineku andmeid kannab GradientPaint. Konstruktoris tuleb määrata kaks punkti ning kummagi punkti juurde kuuluv värv. Nendes punktides vastab joonise värv sinna määratud värvile, punkte ühendaval sirget mööda muutub värv sujuvalt ühest värvist teiseks. Tavajuhul jääb kummagi punkti "selja taha" punktile vastav värv, kuid kui lisada konstruktorisse tõeväärtusmuutuja, siis saab panna värvi tsükliliselt lainetama nii, et laine pikkuseks jääb kahe punkti vahe. import java.awt.*; import java.awt.geom.*; public class Kujund2a extends Canvas{ public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; g.setPaint(new GradientPaint(0, 100, Color.yellow, getSize().width, 100, new Color(0, 200, 100) )); g.fillRect(0, 0, getSize().width, getSize().height); } } Pinna mustriga katmisel tuleks kasutada klassi TexturePaint võimalusi, andes konstruktorile parameetriks pildi millega katta, lisaks ka pildi suuruse ja asukoha, millest alates pilte kõrvuti laduda. Kujundi äärejooned Kujundi (näiteks ellipsi) äärejooned annab BasicStroke meetod createStrokedShape. Nii näiteks on võimalik kujundist vaid äärejooned välja joonistada või siis teise värviga esile tuua. public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; BasicStroke b1=new BasicStroke(15); Shape kujund=b1.createStrokedShape( new Ellipse2D.Float(100, 100, 50, 70) ); g.fill(kujund); g.setColor(Color.green); g.draw(kujund); } Kuna nii ellips on kujund ning ka jämedajoonelise ellipsi äärejooned on samuti kujundid, siis juhul, kui annan äärejoontele jämeduse, võin ka nendelt omakorda äärejooned küsida. Nii saan tulemuseks juba neli joont: kaks üksteisele lähedal asuvat ovaali sees ning teised kaks ovaali välisringis. public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; BasicStroke b1=new BasicStroke(15); BasicStroke b2=new BasicStroke(3); Shape kujund=b1.createStrokedShape( new Ellipse2D.Float(100, 100, 50, 70) ); Shape kujund2=b2.createStrokedShape(kujund); g.draw(kujund2); } Kui sooviksin nende joontega midagi eraldi teha, näiteks neid igaüht isevärvi värvida, siis on mul võimalik kujund joonteks jagada klassi PathIterator abil. Selle abil saan kätte iga joone andmed. Sirgjoonel piisab kahest punktist. Kolme punkti abil määratakse kõverjoon, kus kaks punkti on otspunktideks ning kolmas näitab, milline peab kaar tulema. Nelja punktiga määratud joone puhul on samuti kaks otspunktideks, ülejäänud kahe punkti abil aga määratakse joone suunda otspunktist väljumisel. Uusi kujundeid aitab kombineerida klass GeneralPath. Talle tuleb lihtsalt öelda millise koha peale joon või ring või muu olemasolev kujund paigutada. Kriipsujuku saab tema abil küllalt kergesti valmis ning siis võib seda kasutada nagu iga muud kujundit. public void paint(Graphics alggr){ Graphics2D g=(Graphics2D)alggr; GeneralPath gp=new GeneralPath(); gp.append(new Ellipse2D.Float(20, 50, 40, 40), false); gp.append(new Line2D.Float(40, 100, 40, 150), false); g.draw(gp); } Pilditöötlus Image on java jaoks nii väljastpoolt failist võetud kui ise joonistatud pilt. Tühja pildi joonistamiseks saab luua meetodi createImage abil. Image osutab pildi andmetele, tema kaudu saab pilti kusagil näidata. Pildi muutmiseks tuleb pildilt küsida klass Graphics (või tema alamklass Graphics2D). Selle nn. graafilise konteksti juures on meetodid, mille abil saab pildile midagi (juurde) joonistada. Valmis pilti kuhugile joonistades saab määrata tema kõrgust ja laiust, Graphics2D abil ka keerata või venitada. Võib mitmest pildist ühe kokku kombineerida ning sinna vajadusel jooni, ringe ja muid kujundeid juurde joonistada. Keerulisemate kujundite loomisel on kiiremaks mooduseks ta mälus punkti kaupa välja arvutada ning siis loodud massiivist pilt luua. Nii saab näiteks värviüleminekute juures ilusti igale punktile värvi arvutada ning hoolitseda, et ükski punkt värvita ei jääks. Pilti arvudeks muutes saab pildi andmed näiteks faili salvestada või Interneti kaudu teisele kasutajale saata. Ka on selle abil võimalik pilti analüüsida. Kui pildi käest andmed küsida ning pärast nende muutmist andmetest uus pilt luua, siis saab pilti päris mitut moodi mõjutada. Võib ühe värvi teisega asendada, kujundite piirpindu silendada või teravdada. Suurema mõttetöö korral ka olemasolevast pildist kujundeid eraldada ning nendega omaette midagi ette võtta (näiteks raamatust kopeeritud pildist teksti välja lugeda). Animatsiooni puhul on mõistlik uus ekraanipilt enne mälus valmis teha ning alles siis ekraanile joonistada. Sellisel juhul ei hakka pilt uue kujundi loomise ajal vilkuma. Animatsioon võtab suhteliselt palju ressursse, kuid võtete abil saab liikumist näidata ka aeglasema arvuti peal. Kui taust püsib paigal ning vaid esiplaani kujund liigub, siis piisab tausta korra valmistamisest ning iga uue pildi loomiseks joonistada taust alla ning kujund sobivasse kohta peale. Veelgi enam: piisab, kui taust joonistada vaid selle osa peale, kus liikuv kujund viimati oli (meetod clip laseb määrata, millisesse pildi piirkonda joonistada, mujale suunatud joonistuskäsud jäävad täitmata). Kui liikumine piirdub vaid mõne(teistkümne) pildi vahetamisega, siis on mõistlikum pildid enne välja arvutada/joonistada ning siis ükshaaval ringis vahetada. Kopeerimine Programmide sees ning ka programmide vahel kasutatakse andmete vahetamiseks mälupuhvrit (clipboard). Sinna saab andmeid paigutada ning sealt vajadusel kopeerida. Kui puhver on operatsioonisüsteemi juures ning sinna pääsevad ligi mitmed programmid, siis saab selle abil nende vahel andmeid vahetada. Et andmeid sobiksid, peavad osapooled aru saama andmete formaadist. Kõige lihtsamaks formaadiks on lihtne tekst, kuid selle kaudu saab kõike vahetada, mida on võimalik baitideks muundada. Järgnevast näitest suurem osa kulub kujundusele, kus luuakse raam, pannakse sinna sisse tekstiväli, tekstiala ning menüü. Kopeerimine ja kleepimine asub meetodis actionPerformed, mis käivitub menüüst valiku tegemisel. Vastavalt menüürea nimele käivitatakse tegevus. Meetodi algul küsitakse juurdepääs operatsioonisüsteemi mälupuhvrile. Clipboard clipboard = getToolkit().getSystemClipboard(); Kui antakse korraldus Copy, siis võetakse tekstiväljast tekst ning muudetakse ta StringSelection'iks. Viimatinimetatud klassis on tekst kujul, mida saab mälupuhvrisse panna ning mida teised programmid lugeda mõistavad. StringSelection data = new StringSelection(tf.getText()); Seejärel öeldakse, et mingu see tekst mälupuhvrisse. Meetodi teiseks parameetriks on ClipboardOwner, kellele saadetakse teade puhvri sisu vahetumisest. clipboard.setContents(data, data); Kleepimise puhul võetakse teade mälupuhvrist välja ning pannakse ta tekstialasse. Puhvrist saadakse andmed kätte esialgu tüübina Transferable, mis tuleb seejärel sobivaks tüübiks muundada. Transferable käest on võimalik küsida millisel kujul ta andmeid kannab. Siin aga eeldame, et tegemist on sõnega ning palume tal sellisena need andmed ka välja anda. Transferable clipData = clipboard.getContents(this); String s = (String)(clipData.getTransferData(DataFlavor.stringFlavor)); Tulemuseks on programm, mille abil saab andmeid tekstina programmide vahel vahetada. Selgitust vajab ka ehk menüü loomine. Algul luuakse menüüriba (MenuBar), sinna külge pannakse menüü(d) (Menu) ning viimasesse menüüread (MenuItem). Menüüridadele öeldakse, kellele nende peale vajutamisel teateid saata. import java.io.*; import java.awt.*; import java.awt.datatransfer.*; import java.awt.event.*; public class ClipMe extends Frame implements ActionListener { TextField tf; TextArea ta; public static void main (String args[]) { new ClipMe().show(); } public ClipMe() { super("Clipping Example"); add(tf = new TextField("Welcome"), "North"); add(ta = new TextArea(), "Center"); MenuBar mb = new MenuBar(); mb.add(makeEditMenu()); setMenuBar(mb); setSize(250, 250); } Menu makeEditMenu() { Menu editMenu = new Menu("Edit"); MenuItem mi = new MenuItem("Cut"); mi.addActionListener(this); editMenu.add(mi); mi = new MenuItem("Copy"); mi.addActionListener(this); editMenu.add(mi); mi = new MenuItem("Paste"); mi.addActionListener(this); editMenu.add(mi); mi = new MenuItem("Clear"); mi.addActionListener(this); editMenu.add(mi); return editMenu; } public void actionPerformed (ActionEvent evt) { Clipboard clipboard = getToolkit().getSystemClipboard(); String cmd = evt.getActionCommand(); if (cmd.equalsIgnoreCase("copy")) { StringSelection data = new StringSelection(tf.getText()); clipboard.setContents(data, data); } else if (cmd.equalsIgnoreCase("clear")) { tf.setText(""); } else if (cmd.equalsIgnoreCase("paste")) { Transferable clipData = clipboard.getContents(this); String s; try { s = (String)(clipData.getTransferData(DataFlavor.stringFlavor)); } catch (Exception e) { s = e.toString(); } ta.setText(s); } else if (cmd.equalsIgnoreCase("cut")) { StringSelection data = new StringSelection(tf.getText()); clipboard.setContents(data, data); tf.setText(""); } } } Andmete vedamine (Drag and Drop) Lisaks mälupuhvri abil kopeerimisele püütakse andmete ülekannet ka hiirega vedamise abil kasutajale intuitiivsemaks muuta. Enamik meist on tõenäoliselt hiirega Word'i redaktoris sõnu lauses ringi tõstnud või Windows Exploreri aknas faile ühest kataloogist teise lohistanud. Andmete allikaks või suudmeks saab määrata ükskõik millise komponendi, kes on võimeline hiire teateid vastu võtma. Komponendile tuleb määrata sündmus, mille peale ta end andmete allikaks loeb. Sageli on selleks näiteks hiire vajutus ning lohisemine tema peal vähemalt viie punkti ulatuses. Kui andmed on kord liikuma pandud, saab nende "käekäigu" üle teateid andmeveo kuulari abil, kes teatab hiire sattumisest võimaliku vastuvõtja alasse. Andmete vastuvõtjalgi on kuular. Tema saab teateid enesele sattunud andmetega varustatud hiirest ning on võimeline vastavalt nendele teadetele käituma. Ta saab võrrelda pakutavat andmete tüüpi enese poolt vastu võtta suudetavate andmetüüpidega ning sellest kasutajale teadma andma. Kui hiire klahv lastakse vastuvõtja kohal lahti, siis saabub teade drop ning andmed võib vastu võtta. Siin näites luuakse raam kolme sildiga. Ülemised kaks on andmete allikaks ning nende siltide pealt vedama hakkamisel kaasneb andmetena sildi peal olev kiri. Kolmas silt on vastuvõtja. Kui selle peal vabastakse andmeid kandva kursoriga hiire klahv, siis jääb saabunud tekst sildi sisse. Nii allika kui suudme olen loonud sildi alamklassina. AndmeveoAlguseKuularis on kirjas, mida tuleb teha, kui DragSource poolt loodud DefaultDragGestureRecognizer on märganud, et sildi pealt hakatakse andmeid vedama. Sel puhul võetakse sildi tekst, muutetakse ta Transferable tingimustele vastavaks StringSelection'iks, et teda saaks üle kanda ning siis käivitatakse vedu käsuga startDrag. Parameetriteks on vedamise ajal näidatav kursor, kantavad andmed ning kuular, kellele saadetakse teated andmetega teel toimuva kohta. Suudmel on isend DropTarget, kelle poolt loodud AndmeteSaabumiseKuular saabuvate andmetega tegeleb. Kui suudme kohal lastakse andmehulk, saab ta selle tüüpide sobivuse korral vastu võtta. Esialgu küsitakse meetodi parameetrilt Transferable-tüüpi andmed, sealt oodatud kujul Objectina ning lõpuks tuleb nad kasutatavale kujule muudada. Siis võib nendega edasi toimida, siin näites andmete sees paiknev tekst oma sildile paigutada. import java.awt.*; import java.awt.dnd.*; import java.awt.datatransfer.*; public class Allikas extends Label { private DragSource dragSource; public Allikas(String s) { setText(s); dragSource = DragSource.getDefaultDragSource(); dragSource.createDefaultDragGestureRecognizer( this, DnDConstants.ACTION_COPY, new AndmeveoAlguseKuular()); } class AndmeveoAlguseKuular implements DragGestureListener { public void dragGestureRecognized(DragGestureEvent e) { Transferable andmed = new StringSelection( getText() ); e.startDrag(DragSource.DefaultCopyDrop, andmed, new AndmeveoKuular()); } } class AndmeveoKuular implements DragSourceListener { public void dragDropEnd(DragSourceDropEvent e) { } public void dragEnter(DragSourceDragEvent e) { } public void dragOver(DragSourceDragEvent e) { } public void dragExit(DragSourceEvent e) { } public void dropActionChanged (DragSourceDragEvent e) { } } } import java.awt.*; import java.awt.dnd.*; import java.awt.datatransfer.*; import java.io.*; public class Suue extends Label { private DropTarget dropTarget; public Suue(String s) { setText(s); dropTarget = new DropTarget(this, DnDConstants.ACTION_COPY, new AndmeteSaabumiseKuular(), true); } class AndmeteSaabumiseKuular implements DropTargetListener { public void dragOver(DropTargetDragEvent e) { } public void dropActionChanged(DropTargetDragEvent e) { } public void dragExit(DropTargetEvent e) { } public void dragEnter(DropTargetDragEvent e) { } public void drop(DropTargetDropEvent e) { try{ e.acceptDrop(DnDConstants.ACTION_COPY); Object data = e.getTransferable(). getTransferData(DataFlavor.stringFlavor); setText(data.toString()); }catch(Exception ex){ ex.printStackTrace(); } } } } import java.awt.*; public class Vedamine{ public static void main(String argumendid[]){ Frame f=new Frame("Andmeveo raam"); f.setLayout(new GridLayout(3, 1)); f.add(new Allikas("Karu")); f.add(new Allikas("Rebane")); f.add(new Suue("Vea siia!")); f.setSize(200, 100); f.setVisible(true); } } Swing Lisaks kümnekonnale paketis java.awt asuvale komponendile saab kasutajaga suhtlemiseks kasutada ka paketi javax.swing graafilisi komponente. Nagu eelpool graafikakomponentide peatükis kirjeldatud, palutakse awt-komponendid joonistada operatsioonisüsteemil, swing-komponente joonistatakse Java vahenditega. Sellest tulenevalt näevad esimesed välja nii nagu vastavas operatsioonsiüsteemis tavaks, swingi nupp või silt aga on igal pool tavajuhul peaaegu ühesugune. UIManageri abil aga on võimalik panna ka swing-komponente vastavalt operatsioonisüsteemile välja nägema. Swingi graafikakomponendid algavad tähega J. Tõenäoliselt seetõttu, et neid oleks kerge eristada analoogilistest awt komponentidest. Järgnevas näiteks on raamiks JFrame, selle sees on silt JLabel ning nupp JButton. Nii nupule kui sildile (ja ka mitmetele muudele komponentidele) saab tema ilmestamiseks määrata ikooni. ImageIcon loob ikooni kasutades aluseks pildifaili, kuid vajadusel saab ikooni ka käskude abil joonistada. Kõikidele swingi komponentidele saab määrata ToolTipText'i. Seda näidatakse ekraanile juhul, kui kasutaja on hiirega vastava komponendi peale liikunud. Enamasti vastav tekst seletab komponendi otstarvet või annab kasutajale tegutsemissoovitusi. Korraldus setMnemonic lubab klahvikombinatsiooni (Alt + täht) võrdsustada hiirega nupule vajutamisele. Komponentide raami lisamisel tuleb swingi puhul määrata, millisesse kihti ta paigutada. Harilikult kasutatava alumise kihi saab kätte getContentPane() abil. Pealmist kihti nimetatakse GlassPane ning vahepealsetesse kihtidesse paigutamiseks saab kasutada LayeredPane vahendeid. Kihtidega mängides saab komponente mitmesse kihti paigutada. import java.awt.*; import javax.swing.*; public class Pildid{ public static void main(String argumendid[]){ JLabel silt=new JLabel("Maja silt"); Font suurkiri=new Font("Serif", Font.BOLD+Font.ITALIC, 30); Icon majapilt=new ImageIcon("maja.gif"); silt.setFont(suurkiri); silt.setIcon(majapilt); JButton nupp=new JButton("Maja nupp", majapilt); nupp.setToolTipText("Head vajutamist!"); nupp.setMnemonic(java.awt.event.KeyEvent.VK_M); JFrame f=new JFrame("Sildiraam"); Container p=f.getContentPane(); p.setLayout(new GridLayout(2, 1)); p.add(silt); p.add(nupp); f.pack(); f.setVisible(true); } } HTML-kujundus Swingi komponentidel näidatavat teksti saab HTMLi abil kujundada. Täpne väljanägemine võib sõltuda intepretaatorist, kuid selliselt on programmi väljanägemist lihtsam pilkupüüdvaks muuta kui awt vahenditega kujundades. Nagu lihtsat teksti kandva sildi puhul, nii ka siin on võimalik programmi töö käigus sildi sisu muuta. import javax.swing.*; public class HtmlLabel{ public static void main(String argumendid[]){ JFrame f=new JFrame("Kujundatud silt"); JLabel silt=new JLabel( "

Pealkirja

\n"+ "ja punase tekstiga silt" ); f.getContentPane().add(silt); f.pack(); f.setVisible(true); } } Sisemised raamid Mõnes programmis on näha, et pearaami sees on omaette väiksemad raamid. Nii näiteks Wordi puhul võib iga tekst olla lahti omaette raamis, need aga omakorda suure Wordi raami sees. Sellist olukorda Java keskkonnas saab tekitada JInternalFrame abil. Nemad käituvad JDesktopPane sees samuti nagu harilikud raamid suure ekraani sees. Tema sees paiknevasse paneeli saab lisada komponente nagu ikka. Ka sisemisi raame saab muuta ikooniks alla serva, suurendada ja vähendada. Kui suure raami teateid püüab WindowListener, siis siseraamiga toimuva teada saamiseks aitab InternalFrameListener. Vorm on küll erinev, kuid võimalused samad. import javax.swing.*; public class SiseraamigaRaam{ public static void main(String argumendid[]) throws Exception{ JInternalFrame siseraam1=new JInternalFrame("Esimene"); JInternalFrame siseraam2=new JInternalFrame("Teine", true, true, true, true); siseraam2.getContentPane().add(new JTextArea()); JDesktopPane paneel=new JDesktopPane(); siseraam1.setSize(200, 100); siseraam1.setLocation(10, 80); siseraam1.setVisible(true); paneel.add(siseraam1); siseraam1.setSelected(true); siseraam2.setVisible(true); paneel.add(siseraam2); siseraam2.setSize(150, 150); siseraam2.setLocation(120, 50); JFrame f=new JFrame("Kest"); f.setContentPane(paneel); f.setSize(300, 300); f.setVisible(true); } } Värvivalija Kasutajapoolseks värvi valimiseks saab vajadusel kirjutada ise dialoogiakna, kust hiirega omale sobiv värv leida võimalik on. Swingi all aga on programmeerimisvaeva vähendamiseks loodud komponent JColorChooser, mille abil kasutaja saab pakutavate hulgast sobiva värvi välja valida. Värvivalikuks pakub komponent kolme võimalust: esimesel juhul saab hiirega vajutada sobivat värvi ruudule. Teisel puhul saab valida värvi ning teiselt skaalalt värvile vastava tumeduse. Kolmandas valikuaknas lastakse kasutajal määrata punase, rohelise ning sinise vahekord loodavas värvis. Vaikimisi kujul näitab komponent otsitavat värvi mitmesuguste kujundite ning ingliskeelse teksti peal. Selle kujundi saab aga vajadusel programmi ilmele sobivama või hoopis tühja pisikese paneeli vastu välja vahetada (järgnevas näites vastav rida välja kommenteeritud). Värvivaliku registreerimiseks ning temale reageerimiseks saab kasutada kuularit. Värvivaliku klassi juurde on loodud meetodid ka dialoogiakna kaudu värvi valimiseks. import javax.swing.*; import javax.swing.event.*; public class Varvivalik{ public static void main(String argumendid[]){ final JTextArea tekstiala=new JTextArea("Värvilised tervitused"); final JColorChooser valija=new JColorChooser(); // valija.setPreviewPanel(new JPanel()); valija.getSelectionModel().addChangeListener( new ChangeListener(){ public void stateChanged(ChangeEvent e){ tekstiala.setForeground(valija.getColor()); } } ); JFrame f=new JFrame("Värvivalik"); java.awt.Container p=f.getContentPane(); p.add("Center", tekstiala); p.add("South", valija); f.setSize(300, 500); f.setVisible(true); } } Failinime valija Ka failinime failinime valimise dialoogi saab ise suurema vaevata kirjutada, kuid swingi klass JFileChooser on juba vastavaks otstarbeks loodud ilusasti kujundatud komponent. Saab määrata, millisest kataloogist alates faili otsima saab hakata. Meetod showOpenDialog avab dialoogi ning programm jääb kasutajapoolset valikut ootama. Kui fail on valitud või valimine tühistatud, läheb programm edasi ning komponendi meetodite kaudu saab teada kasutaja vastuse. Kui soovitud fail on käes, saab temaga samuti toimida nagu failiga ikka, s.t. tema kohta infot küsida, sealt lugeda või sinna kirjutada. import javax.swing.*; import java.io.*; public class Failivalik{ public static void main(String argumendid[]){ JFileChooser valija=new JFileChooser(new File(".")); valija.showOpenDialog(new JFrame()); System.out.println("Valiti "+valija.getSelectedFile()); } } Failivalikut saab veidi programmiga kohandada. Näiteks võib määrata avamisnupu peal olevat kirja. Samuti võib lubada korraga mitut faili valida. Järgnevas näites lisatakse filtri abil võimalus eraldi vaid pildifailide hulgast kasutajal sobivat otsida. Filtri loomisel tuleb üle katta meetodid accept ning getDescription. Esimesele antakse järjekorras ette kõik failid, mida parasjagu oleks võimalik valida. See meetod peab igaühe kohta neist ütlema, kas vastavat faili kasutajale näidata või mitte. Meetod getDescription väljastab filtrile sobivate failide ühisnimetaja, siin näites "Pildifailid". import javax.swing.*; import javax.swing.filechooser.FileFilter; import java.io.*; public class Failivalik2{ public static void main(String argumendid[]){ JFileChooser valija=new JFileChooser(new File(".")); valija.addChoosableFileFilter(new Pildifilter()); valija.showDialog(new JFrame(), "Vali fail"); System.out.println("Valiti "+valija.getSelectedFile()); } } class Pildifilter extends FileFilter{ public boolean accept(File f){ String failinimi=f.getName(); if(failinimi.endsWith(".gif")|failinimi.endsWith(".jpg")) return true; else return false; } public String getDescription(){ return "Pildifailid "; } } Puud Infohulgas orienteerumiseks saab andmeid esitada puuna. Siis on kasutajal võimalik ekraanilt kasutu kõrvaldada ning hierarhia abil enesele sobiv üles leida. Puu abil on näiteks esitatud failid ja kataloogid FileManageris ja WindowsExploreris. Puusse saab lisada kõiki objekte. Vaikimisi juhul määrab JTree ise okstele ja lehtedele sobivad ikoonid ning objekti kirjelduseks kasutab sõnet mille väljastab ta toString meetod. Vajadusel aga saab nii ikooni kui kirjeldust muuta. Puu hierarhia saab kokku panna DefaultMutableTreeNode abil. Kui ta on reas viimane, on ta leht, keskel oks ning algul juur. Puu ekraanile kuvamiseks tuleb luua JTree, kellele määrata juur, millest alates elemente näidata tuleb. Siin näites on pandud juureks ülikool, tema alla paar teaduskonda ning teaduskonna alla mõni õppetool. Võib jääda mulje, nagu tuleks puu loomiseks hirmus palju kirjutada, kuid suuremate andmehulkade korral saab luua või kasutada olemasolevaid alamprogramme ning puu tegemine polegi kuigi keeruline. import javax.swing.*; import javax.swing.tree.*; public class Puu{ public static void main(String argumendid[]){ DefaultMutableTreeNode juur=new DefaultMutableTreeNode("Ülikool"); DefaultMutableTreeNode teaduskond=new DefaultMutableTreeNode( "Matemaatika-Loodusteaduskond"); teaduskond.add(new DefaultMutableTreeNode("Informaatika õppetool")); teaduskond.add(new DefaultMutableTreeNode("Bioloogia õppetool")); juur.add(teaduskond); juur.add(new DefaultMutableTreeNode("Kultuuriteaduskond")); JTree puu=new JTree(juur); JFrame f=new JFrame("Puu"); f.getContentPane().add(puu); f.setSize(200, 200); f.setVisible(true); } } Kasutaja tegevuse registreerimiseks saab tarvitada mitmesuguseid kuulareid. Saab teada, millal ta puu nähtavat osa suurendas või vähendas, mis osa puust nähtav on, millal mõni element valiti. Ka pärast puu loomist saab temasse elemente lisada ja sealt eemaldada. Käsklus puu.putClientProperty("JTree.lineStyle", "Angled"); palub puu osad omavahel joontega ühendada. Siin näites trükitakse igal valikul välja valitud komponent. TreeSelectionEvent'i meetod getPath() annab tulemuseks kogu rea alates juurest kuni märgitud leheni. Selle kaudu oleks võimalik ükskõik millise tee peale jääva komponendi poole pöörduda. Meetod getLastPathComponent() annab tulemuseks tee viimase elemendi, ehk selle, millele kasutaja vajutas. import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; public class Puu2a{ public static void main(String argumendid[]){ DefaultMutableTreeNode juur=new DefaultMutableTreeNode("Ülikool"); DefaultMutableTreeNode teaduskond=new DefaultMutableTreeNode( "Matemaatika-Loodusteaduskond"); teaduskond.add(new DefaultMutableTreeNode("Informaatika õppetool")); teaduskond.add(new DefaultMutableTreeNode("Bioloogia õppetool")); juur.add(teaduskond); juur.add(new DefaultMutableTreeNode("Kultuuriteaduskond")); JTree puu=new JTree(juur); puu.addTreeSelectionListener( new PuuKuular() ); puu.putClientProperty("JTree.lineStyle", "Angled"); JFrame f=new JFrame("Puu"); f.getContentPane().add(puu); f.setSize(200, 200); f.setVisible(true); } } class PuuKuular implements TreeSelectionListener{ public void valueChanged(TreeSelectionEvent e){ System.out.println(e.getPath().getLastPathComponent()); } } Paigutus JSplitPane võimaldab temale eraldatud pinna jaotada kahe komponendi vahel. Tuleb vaid määrata, kas pind jaotatakse kaheks horisontaalselt või vertikaalselt ning komponendid, mis kummasegi ossa panna. Lisameetoditega saab määrata ja muuta jagamise kohta, samuti kasutajapoolset vahepiiri nihutamise võimalusi. Jagatud paneelidena näevad välja mitut lehte sisaldavad brauseri aknad, kus vasakul näiteks sisukord ja paremal sisu. import javax.swing.*; public class Jaotuspaneel{ public static void main(String argumendid[]){ JSplitPane paneel=new JSplitPane( JSplitPane.VERTICAL_SPLIT, new JButton("Ülemine"), new JToggleButton("Alumine") ); JFrame f=new JFrame("Jagatud raam"); f.getContentPane().add(paneel); f.setSize(200, 200); f.setVisible(true); } } Kui määranguaknas on võimalusi rohkem kui kasutajale korraga mõistlik näidata on, siis JTabbedPane abil saab muud paneelid ülekuti paigutada. Sarnaselt on loodud näiteks Exceli lahtrimäärangute dialoogiaken, kus ühel paneelil saab määrata andmete tüüpi, teisel kujundust jne. Soovi korral saab paneelivaliku nuppudele lisada ikoonid, eemaldada, vahetada ja lisada paneele, mõne valiku tegemist keelata, automaatselt soovitud paneel esile tuua ning mitmel moel kasutaja tegevuse kohta teateid saada. import javax.swing.*; public class Valikupaneel{ public static void main(String argumendid[]){ JPanel p1=new JPanel(new java.awt.GridLayout(2,1)); p1.add(new JButton("Ülemine")); p1.add(new JButton("Alumine")); JTabbedPane paneel=new JTabbedPane(); paneel.add("Esimene", p1); paneel.add("Teine", new JLabel("Suur silt")); JFrame f=new JFrame("Valikupaneeli näide"); f.getContentPane().add(paneel); f.setSize(200, 200); f.setVisible(true); } } Swingi kokkuvõte Swingi paketis on hulk klasse, mis peaksid aitama kasutajal programmiga meeldivalt suhelda. Samuti on püütud nende loomisel silmas pidada erivahendite kasutajaid (ekraanilt lugejad, lihtsustatud klaviatuurid). Lisaks eelnevalt kommenteeritutele kuuluvad siia paketti veel mitmed sageli kasutust leidvad komponendid: JMenuBar, JMenu, JSeparator, JMenuItem, JCheckBoxMenuItem ja JPopupMenu menüüde loomiseks; JToolBar tööriistariba tarvis. Action-liidese abil saab samastada menüüelemendi ning tööriistariba nupu. Tabelisse paigutada aitab JTable ning teksti näidata ja redigeerida saab JEditorPane ning mitme muu klassi abil. 13