Komponendid Arvutusvõimsuste ja programmimahtude kasvades on programmide arengut pidurdama jäänud programmeerija tööaeg ning programmides leiduvad vead. Loodeti, et tupikust päästab objektitehnika, kuid ka see ei osutunud imevahendiks. Nii nagu protseduurid aitasid liigendada jadaprogramme, aitasid objektid süstematiseerida protseduure. Kui aga klasse saab palju, on nendega samuti raske toime tulla. Javas on loodud lisatasemeks paketid, kuhu koondatakse sarnaste omadustega klassid. See aitab sobivat klassi leida. Samuti saavad ühes paketis paiknevad klassid lubada vastastikku üksteise omadusi kasutada. Järgmiseks lahenduseks on pakutud komponenttehnoloogia. Selle järgi pannakse programm kokku kasutamisvalmis tükkidest. Näiteks võivad tükkideks olla kalender, liikuv auto, draiverite komplekt. Programmeerijal tuleb nad sobivalt ühendada ning loota, et tulemusena valmib kasutajasõbralik programm. Ühte komponenti võivad kuuluvad klassid eri pakettidest, samuti võib võrguprogrammide puhul üks komponent asetseda laiali mitmes masinas. Komponente saab luua mitmetes programmeerimiskeeltes. Nii on Java komponentideks oad (Bean), Microsoftil ActiveX komponenid. Et nad omavahel suhelda saaksid, on kirjeldatud sillad, millisel kujul komponendid andmeid vahetada saavad. Oad Java abil loodud komponente nimetatakse ubadeks (bean). Et neid võiks julgelt kõikjal kasutada, on neil samasugused turvapiirangud nagu rakenditelegi. Vabalt saab omale laadida BDK, mille abil saab hiirega komponente kokku lohistada ning rakendiks muuta. Seal saab kasutada ka neid komponente, mille loomisel pole BDK võimalusi silmas peetud, kuid järgides komponentidevahelise suhtlemise reegleid on võimalik oma komponenti teistega paremini suhtlema panna. Trükkimine Lühike näide Lihtsaks trükkimiseks tuleb luua liidest Printable realiseeriv klass, kus meetodis print öeldakse, kuidas tuleb trükkida. Printerisse joonistamine käib samuti graafilise konteksti Graphics abil nagu ekraanile või mällugi joonistamine. Meetodi print parameetrina tuleva PageFormat'i abil saab teada, kui suure trükitava lehega tegemist on ning millisele osale lehest on võimalik joonistada. Enamasti on lehe serva määratud vaikimisi selline osa, kuhu joonistada ei saa. Joonistuskõlbuliku osa alguse x-koordinaadi annab getImageableX(). Samuti saab PageFormat'i käest küsida y-koordinaati ning joonistatava ala kõrgust ja laiust. Siin näites transleeritakse graafilise konteksti nullkoht joonistatava ala algusesse. Kolmanda parameetrina tulev leheküljenumber näitab, millist lehekülge soovitakse trükkida. Isendil on täiesti võimalik mitmele leheküljele trükkida. Lihtsalt tuleb meetodis print igale leheküljenumbrile vastavalt reageerida. Kui vastava numbriga lehekülge ei soovita trükkida, peab meetod tagastama väärtuse Printable.NO_SUCH_PAGE, muul juhul Printable.PAGE_EXISTS. Trükkimise käivitamiseks luuakse isend tüübist PrinterJob, määrakse, milline Printable oskusega isend trükitöö ära teeb ning siis palutakse trükkima hakata. import java.awt.print.*; import java.awt.*; public class Trykk1{ public static void main(String argumendid[]) throws PrinterException{ PrinterJob pj=PrinterJob.getPrinterJob(); pj.setPrintable(new Trykitoo1()); pj.print(); } } class Trykitoo1 implements Printable{ public int print(Graphics g, PageFormat pf, int lk) throws PrinterException{ if(lk>0) return Printable.NO_SUCH_PAGE; g.translate((int)pf.getImageableX(), (int)pf.getImageableY()); g.drawOval(10, 10, 200, 200); return Printable.PAGE_EXISTS; } } Komponendi trükkimine Kuna nii ekraanile kui printerisse joonistab klassi Graphics järglane, siis saab joonistamisel kasutada sama meetodit. Siin näites joonistatakse mõlemasse meetodi paint abil. Programm loob ekraanile nupu ning omaloodud komponendi Kiri2. Nupule vajutades trükitakse Kiri2 printerisse. Trükkimine on korraldatud nii, et vajutamise peale trükitakse Kiri2 tüüpi isendit 2 lehekülge, kummalegi lehele joonistatakse tema kujutis ning lehe alla kirjutatakse lehekülje number. Klassi PrinterJob meetod printDialog kutsub välja dialoogiakna, kust kasutaja saab määrata printerit ning väljastatavate lehekülgede numbreid ja koopiate arvu. import java.awt.*; import java.awt.event.*; import java.awt.print.*; class Kiri2 extends Canvas implements Printable { public void paint(Graphics g) { g.setColor(Color.black); int W = (int)getSize().getWidth(); int H = (int)getSize().getHeight(); g.drawRect(1, 1, W-3, H-3); g.drawString("Tere!", W/2, H/2); } public int print(Graphics g, PageFormat pf, int lk) throws PrinterException { if (lk >= 2) { return Printable.NO_SUCH_PAGE; } g.translate((int)pf.getImageableX(), (int)pf.getImageableY()); g.setColor(Color.black); paint(g); g.drawString("lk nr. "+(lk+1), 100, (int)pf.getImageableHeight()-50); return Printable.PAGE_EXISTS; } } public class Trykk2 extends Panel implements ActionListener { Kiri2 kiri=new Kiri2(); Button b = new Button("Tryki"); public Trykk2() { b.addActionListener(this); add(b); kiri.setSize(100, 50); add(kiri); } public void actionPerformed(ActionEvent e) { PrinterJob pj = PrinterJob.getPrinterJob(); pj.setPrintable(kiri); try { if(pj.printDialog()) pj.print(); } catch (Exception PrintException) { } } public static void main(String s[]) { Frame f = new Frame("Trykkimisraam"); f.add("Center", new Trykk2()); f.pack(); f.setSize(400,300); f.show(); } } Trükitava ala suuruse muutmine Trükitava ala suurust lehel saab ka ise muuta. Sel juhul tuleb PageFormat'i käest küsida Paper tüüpi isend, talle määrata soovitud suurusega joonistusala. Siis määrata PageFormat'ile vastav paber ning paluda PrinterJob'il vastava PageFormat'i järgi trükkida. Siin näites luban trükkida lehel servast serva. import java.awt.print.*; import java.awt.*; public class Trykk3{ public static void main(String argumendid[]) throws PrinterException{ PrinterJob pj=PrinterJob.getPrinterJob(); PageFormat pf=pj.defaultPage(); Paper p=pf.getPaper(); p.setImageableArea(0, 0, p.getWidth(), p.getHeight()); pf.setPaper(p); pj.setPrintable(new Trykitoo3(), pf); pj.print(); } } class Trykitoo3 implements Printable{ public int print(Graphics g, PageFormat pf, int lk)throws PrinterException{ if(lk>0) return Printable.NO_SUCH_PAGE; g.drawOval(0, 0, 300, 200); return Printable.PAGE_EXISTS; } } Trükitava ala suurust saab lasta ka kasutajal dialoogiakna abil määrata. Sellise akna manab ekraanile pageDialog. public class Trykk3a{ public static void main(String argumendid[]) throws PrinterException{ PrinterJob pj=PrinterJob.getPrinterJob(); pj.setPrintable(new Trykitoo3a(), pj.pageDialog(pj.defaultPage())); pj.print(); } } Lisavõimalused Printable liidese abil saab trükkida ühesuguse suurusega lehekülgi. Kui peaks aga vaja olema ühte trükitavasse dokumenti kokku panna mitmesuguseid (näiteks püsti- ning põikiformaadis) lehti, siis tuleb algul panna kirjutatavatest lehtedest kokku Book ning seda trükkima hakata. 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 ilusti vahetada saaks, 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); } }