Graphics2D
Graafiline kontekst, joone ja tausta omadused, kujundid.
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 dokumentatsioonist).
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);
}
}
Punktiirjoon
Punktiiri puhul tuleb määrata, millise pikkusega on punktiiri kriipsud, lisaks sellele hulk joontega seotud
andmeid. Kuid kui korra on sulepea (Stroke) valmis tehtud, võib sellega rahumeeli jooni tõmmata nii palju
kui vaid soovid – nii nagu eelmises näites. Kui vaadata sulepea koostamise käsklust
BasicStroke bs1=new BasicStroke(
15, ots, yhendus, yhendusemaxpikkus, punktiir, punktiirinihe);
siis 15 tähendab tõmmatava joone laiust. Ots näitab, millised (ümarad, kandilised) tuleb joone otsad teha.
Muutuja yhendusemaxpikkus on enamasti tarbetu, vaja võib teda minna vaid siis, kui miskis kujundis
ühinevad jooned väga väikese nurga all ning tekib oht pika väljaulatuva nurga moodustumiseks. Siis selle
muutuja järgi vaadatakse, mitmest punktist vastav nurk pikemaks ei tohi minna. Eelnevalt loodud massiiv
punktiir näitab, kuidas punktiirjoones vahelduvad kriipsud ja vahed. Nagu siin näites eespoolt võib piiluda,
on esimese kriipsu pikkuseks määratud 5 punkti, sellele peaks järgnema viieteistpunktiline vahe. Edasi
kümnepunktiline kriips ning selle järele kahekümnepunktine vahe. Siis taas algusest peale, et
kahekümnepunktise vahe järele jälle viiene kriips, siis viieteistkümnene vahe ning nõnda edasi. Muutujast
punktiirinihe vaadatakse, kus kohalt mustriga peale hakata. Kui nihe on 0, siis algab esimene kriips joone
otsast. Kui mõni suurem arv, siis on esimese kriipsu algus vastavalt nihutatud.
Teine BasicStroke on loodud ühe käsuga, enamjaolt on väärtused otse konstruktorisse kirjutatud.
new float[]{15} loob üheelemendilise massiivi otse kohapeal.
import java.awt.*;
import java.awt.geom.*;
import java.applet.*;
public class Punktiir1 extends Applet{
float laius=5;
int ots=BasicStroke.CAP_ROUND, yhendus=BasicStroke.JOIN_MITER;
float yhendusemaxpikkus=10;
float[] punktiir={5, 15, 10, 20}; //kriips, vahe, kriips, vahe, ...
float punktiirinihe=0;
BasicStroke bs1=new BasicStroke(
15, ots, yhendus, yhendusemaxpikkus, punktiir, punktiirinihe);
BasicStroke bs2=new BasicStroke(laius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND,
yhendusemaxpikkus, new float[]{15}, 2);
public void paint(Graphics g){
Graphics2D g2=(Graphics2D)g;
Line2D l1=new Line2D.Float(10, 10, 10, 200);
Rectangle2D r1=new Rectangle2D.Float(40, 10, 150, 190);
g2.setStroke(bs1);
g2.draw(l1);
g2.setStroke(bs2);
g2.draw(r1);
}
public static void main(String argumendid[]){
Frame f=new Frame("Punktiir");
f.add(new Punktiir1());
f.setSize(250, 250);
f.setVisible(true);
}
}
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);
}
}
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);
}
}
Muster
Et taustaks saaks mustrit panna, selleks tuleb kõigepealt mustripilt koostada või välja otsida ning alles
seejärel saab määrata, et joonistatava kujundi värviks on pidevalt korduv loodud muster. Järgnevas näites
on mustritükiks lihtsalt roheline ring mustal taustal. BufferedImage bi hoiab eneses loodavat pilti,
TexturePaint tp aga juba hoolitseb, kuidas pilt korralikult õigesse kohta paigutada. Konstruktoris luuakse
pildile graafiline kontekst, mille abil joonistatakse pildile 20*20 punkti laiune roheline ring. Rida tp=new
TexturePaint(bi, new Rectangle(0, 0, 20, 30)); tähendab, et olemasolev pilt (algse suurusega 20*20
punkti) asub loodud TexturePaint'is olema 20 punkti lai ning 30 kõrge, seega pikkust pidi välja venitatud.
Joonistuskäsu paint sees võetakse pakutud Graphics vastu Graphics2D-na, et õnnestuks vajalikke
käske (setPaint) kasutada. Edasi joonistatakse pinnale ristkülik. Kuna aga joonistusmustriks oli määratud
pilt, siis loodud ristkülik näebki välja mustrilisena.
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.applet.*;
public class Mustritaust extends Applet{
BufferedImage bi=new BufferedImage(
20, 20, BufferedImage.TYPE_INT_RGB);
TexturePaint tp;
public Mustritaust(){
Graphics2D big=bi.createGraphics();
big.setColor(Color.green);
big.drawOval(0, 0, 20, 20);
tp=new TexturePaint(bi, new Rectangle(0, 0, 20,
30));
}
public void paint(Graphics g){
Graphics2D g2=(Graphics2D)g;
Rectangle2D r1=new Rectangle2D.Float(30, 10, 150,
190);
g2.setPaint(tp);
g2.fill(r1);
}
public static void main(String argumendid[]){
Frame f=new Frame("Muster taustaks");
f.add(new Mustritaust());
f.setSize(250, 250);
f.setVisible(true);
}
}
Värviüleminekuga tekst
Joonistusala piiramist ning värviüleminekut kombineerides saab päris keeruka ja ilusa kujundusega
pildi kokku panna.
import java.awt.*;
import java.awt.geom.*;
import java.awt.font.*;
public class Kujund2 extends Canvas{
public void paint(Graphics alggr){
Graphics2D g=(Graphics2D)alggr;
TextLayout tl=new TextLayout("Kassikene",
new Font("Arial", Font.PLAIN, 100),
new FontRenderContext(null, false, false)
);
g.setPaint(new GradientPaint(200, 0, Color.yellow,
200, getSize().height, new Color(255, 200, 0)
));
g.fillRect(0, 0, getSize().width, getSize().height);
g.clip(tl.getOutline(AffineTransform.getTranslateInstance(20, 120)));
g.setPaint(new GradientPaint(0, 100, new Color(66, 66, 0),
getSize().width, 100, new Color(0, 200, 100)
));
g.fillRect(0, 0, getSize().width, getSize().height);
}
}
Soovides jätta all olevat pilti joonistatava kujundi alt läbi paistma, tuleb enne peale joonistamist
määrata Composite, mis paluks peale joonistataval alumine vaid osaliselt ära katta ning jätta tulemuseks
vanaga segatud värvid.
import java.awt.*;
import java.applet.*;
public class Labipaistmine extends Applet{
public void paint(Graphics g){
Graphics2D g2=(Graphics2D)g;
g2.setColor(Color.red);
g2.fillRect(20, 50, 100, 100);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.7f));
//70% joonistab, 30% paistab alt läbi
g2.setColor(Color.blue);
g2.fillRect(60, 60, 100, 70);
}
public static void main(String argumendid[]){
Frame f=new Frame("Läbipaistmine");
f.add(new Labipaistmine());
f.setSize(250, 250);
f.setVisible(true);
}
}
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);
}
Äärejoonte äärejooned
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);
}
Kujundi koostamine
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);
}
Tehted kujunditega
Kujundit luues on võimalik olemasolevatega ka keerukamaid tehteid teha. Näiteks kui soovida
koostada kuuveerandikku, siis võib kõigepealt teha ühe väiksema ringi ning siis sellest lahutada maha
suurem ring, nii et vaid väike osa algsest jääb alles. All olevas näites on võetud kas osaliselt kattuvat
ristkülikut ning näidatud, mis tehteid nendega läbi viia saab. Liites esimesele juurde teise saame kujundite
ühendi, kus mõlema kujundi pinnad on liidetud. Ühisosa tekitab käsk intersect – alles jääb vaid osa
pinnast, mis kuulub mõlema kujundi koosseisu. Lahutamise (subtract) korral jääb esialgsest pinnast alles
vaid osa, mis teise alla ei kuulu. Välistuse (exclusiveOr) puhul jääb mõlemast pinnast alles vaid osa, kus üks
pind teisega ei kattu.
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.event.*;
import java.applet.*;
public class Pind extends Applet implements ItemListener{
Area r1=new Area(new Rectangle2D.Float(20, 50, 100, 100));
Area r2=new Area(new Rectangle2D.Float(60, 60, 100, 70));
Choice valik=new Choice();
public Pind(){
valik.add("Ühend");
valik.add("Ühisosa");
valik.add("Vahe");
valik.add("Välistus");
add(valik);
valik.addItemListener(this);
}
public void paint(Graphics g){
Area pind=new Area();
pind.add(r1);
String s=valik.getSelectedItem();
if(s.equals("Ühend"))pind.add(r2);
if(s.equals("Ühisosa"))pind.intersect(r2);
if(s.equals("Vahe"))pind.subtract(r2);
if(s.equals("Välistus"))pind.exclusiveOr(r2);
Graphics2D g2=(Graphics2D)g;
g2.setColor(Color.blue);
g2.fill(pind);
g2.setColor(Color.red);
g2.draw(pind);
}
public void itemStateChanged(ItemEvent e){
repaint();
}
public static void main(String argumendid[]){
Frame f=new Frame("Muster taustaks");
f.add(new Pind());
f.setSize(250, 250);
f.setVisible(true);
}
}
Ülesandeid
· Joonista ring joone laiusega 25 punkti.
· Määra joonistusvärviks üleminek punaselt kollasele
· Määra taustamustriks väikesed kriipsujukud.
· Muuda joon punktiiriks.
· Määra joonistusala nii, et sinna jääb vaid osa ringjoont
· Jäta taust ringi alt veidi läbi paistma.
Swing
Operatsioonisüsteemist sõltumatud komponendid, kujundus
Lisaks kümnekonnale paketis java.awt asuvale komponendile saab kasutajaga suhtlemiseks
tarvitada ka paketi javax.swing graafilisi komponente. Nagu 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. Näiteks koostab JDesktopPane tüüpi komponendi eraldi staatiline
meetod. Rakendis paigutatakse komponent ekraanile init meetodi sees, käsurealt käivitades luuakse raam
ning siis paigutatakse komponent raami sisse.
import javax.swing.*;
public class SiseraamigaRaam extends JApplet{
static JDesktopPane looRaamiPaneel(){
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);
try{siseraam1.setSelected(true);}catch (Exception e){}
siseraam2.setVisible(true);
paneel.add(siseraam2);
siseraam2.setSize(150, 150);
siseraam2.setLocation(120, 50);
return paneel;
}
public void init(){
getContentPane().add(looRaamiPaneel());
}
public static void main(String argumendid[]) throws Exception{
JFrame f=new JFrame("Kest");
f.setContentPane(looRaamiPaneel());
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 võib 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.*;
import java.awt.BorderLayout;
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(tekstiala, BorderLayout.CENTER);
p.add(valija, BorderLayout.SOUTH);
f.setSize(300, 500);
f.setVisible(true);
}
}
Failinime valija
Ka 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());
}
}
D:\Kasutajad\jaagup\java\naited\gr\swing>java Failivalik
Valiti D:\Kasutajad\jaagup\java\Allikas.java
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 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 selle toString meetod.
Vajadusel aga võib nii ikooni kui kirjeldust muuta.
Puu hierarhia saab kokku panna DefaultMutableTreeNode abil. Kui element 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.*;
import java.awt.BorderLayout;
public class Puu2a extends JApplet{
static JTextField tekstikast=new JTextField();
static JPanel looPuu(){
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");
JPanel paneel=new JPanel(new BorderLayout());
paneel.add(puu);
paneel.add(tekstikast, java.awt.BorderLayout.SOUTH);
return paneel;
}
public void init(){
getContentPane().add(looPuu());
}
public static void main(String argumendid[]){
JFrame f=new JFrame("Puu");
f.getContentPane().add(looPuu());
f.setSize(200, 200);
f.setVisible(true);
}
}
class PuuKuular implements TreeSelectionListener{
public void valueChanged(TreeSelectionEvent e){
Puu2a.tekstikast.setText(e.getPath().getLastPathComponent()+"");
}
}
Paigutus
Jaotuspaneel
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.
Kui koodi vaadata, siis üles on pandud JButton, alla JBoggleButton. Viimase omapäraks on, et
esimest korda vajutades jääb nupp sisse (tumedaks), alles teisel korral tuleb välja tagasi.
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);
}
}
Valikupaneel
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);
}
}
Tööriistariba
Ka sellenimeline abivahend on Swingi all täiesti olemas. Kui on soovi nuppe ja muid tööriistu oma
silma järgi ümber paigutada, siis tööriistariba peaks selleks parim valik olema. Kokku saab selle panna
nagu tavalise paneeli, kuid edaspidi on loodud paneeli võimalik pea vabalt paigutada. Riba saab liigutada nii
omaette raamaknana kui paigutada vabalt iga BorderLayout'i vaba serva peale. Piisab vaid riba
lohistamisest õige koha lähedusse, kui see juba haakub. Paigalt eemale saab ka täiesti rahumeeli lohistada.
Et JAppleti vaikimisi paigutushalduriks ongi BorderLayout, siis neli külge on tööriistaribade jaoks kohe
kasutatavad.
Riba ennast annab koostada ning valmis komponente sinna peale panna paari käsuga. Pea pool
näiteprogrammist on kulunud ovaali kujutava pildiga ikoone loova klassi valmistamiseks, mille abil on
hõlbus äratuntava pildiga nuppe toota. Ikooni loomiseks tuleb teha liidest Icon realiseeriv klass. Siin on see
paigutatud Tooriistariba sisemiseks klassiks, et oleks viimasele alati kättesaadav ning kopeerides kaduma ei
läheks. Iseenesest aga võib loodav ikoone tootev klass olla rahumeeli eraldi klassina, sel juhul on võimalik
ovaalseid ikoone ka teiste programmide kasuks luua. Meetodis paintIcon tuleb kirja panna, milline ikoon
välja näeb, getIconWidth() ning getIconHeight() annavad ikooni soovitud suuruse.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Tooriistariba extends JApplet {
public Tooriistariba() {
JToolBar riba = new JToolBar();
JButton nupp = new JButton("Nupp");
riba.add(nupp);
riba.addSeparator();
riba.add (new Checkbox ("Märkeruut"));
getContentPane().add (riba, BorderLayout.NORTH);
riba = new JToolBar();
Icon icon = new OvaalneIkoon(Color.red);
nupp = new JButton(icon);
riba.add(nupp);
icon = new OvaalneIkoon(Color.blue);
nupp = new JButton(icon);
riba.add(nupp);
icon = new OvaalneIkoon(Color.green);
nupp = new JButton(icon);
riba.add(nupp);
riba.addSeparator();
icon = new OvaalneIkoon(Color.magenta);
nupp = new JButton(icon);
riba.add(nupp);
getContentPane().add (riba, BorderLayout.SOUTH);
}
class OvaalneIkoon implements Icon {
Color varv;
public OvaalneIkoon (Color c) {
varv = c;
}
public void paintIcon (Component c, Graphics g,
int x, int y) {
g.setColor(varv);
g.fillOval (
x, y, getIconWidth(), getIconHeight());
}
public int getIconWidth() {
return 20;
}
public int getIconHeight() {
return 10;
}
}
public static void main(String[] argumendid){
JFrame f=new JFrame("Tööriistaribad");
f.getContentPane().add(new Tooriistariba());
f.setSize(200, 200);
f.setVisible(true);
}
}
Ennistamine
Kui üheksakümnendate aastate algul Word 2-te käsu tagasi võtmise võimalus sisse pandi, siis
tundus olevat tegemist uue tähelepanuväärse ja omapärase lahendusega. Nüüd on kasutajad programmide
juures pea alatise tagasivõtmise võimalusega nii harjunud, et panevad imeks, kui mõne käsu tagajärgi pole
võimalik olematuks teha ning peab enne näpuliigutust hoolikalt läbi mõtlema, mis tehtu tulemuseks võib olla.
Isegi terveid kataloogitäisi andmed saab Windows Exploreris paigast nihutada ning tagasi panna ilma, et
selle peale suuremaid raskusi tekiks. Samuti kannatab mitmeski kohas mitte ainult üht või paari käsku tagasi
võtta, vaid annab pea sammhaaval kogu töö algusesse minna.
Sellised võimalused ei teki töösse iseenesest, selle loomiseks tuleb programmi kirjutamisel kõvasti
hoolt kanda. Samuti peab arvestama, et igat asja pole siiski võimalik tagasi võtta. Kui kord on soovimatu
sisuga kirjad teistele inimestele laiali saadetud, siis on nad teel ja kohal ning meie loodud programmil pole
nende kaotamiseks võimalik enam midagi ette võtta. Parimal juhul annab tagasivõtmise käsu juures
sihtkohta uus kiri saata, et ärgu vastuvõtja eelmise saabunud kirja sisu liialt südamesse võtku.
Tagasivõtmist annab koodi sisse mitut moodi ehitada, kuid Swingi vahendite juures on eraldi selle
tarbeks loodud UndoManager, mis peaks aitama suuremate tööde puhul toimingule süsteemsemalt läheneda.
Et tööd saaks pärast ilusti tagasi võtta, tuleb iga tehtud samm lisada ennistushaldurisse ning iga sammu
juures peab olema kirjas, kuidas samm teha ning kuidas tagasi võtta. Niimoodi tekib sammude ahel, mida
mööda on pärastpoole mugav edasi ja tagasi käia.
Allpool olevas näites on tagasivõetava programmi koostamine läbi mängitud lihtsa pildiredaktori
peal, millele saab joonistada vaid ringe. Lisatud on nupud käskude tagasi võtmiseks ning sama teed pidi
edasi liikumiseks.
Iga hiirevajutusega lisatakse näidatavate ringide nimistusse (Vector) (muutuva pikkusega massiiv)
punkti andmed, mis tähistavad hiirevajutuse asukohta. Nii on mälus kirjas, kuhu joonistusvajaduse korral
ringid tekitada ning vähemasti iga operatsiooni järel palutakse ekraanipilt uuendada.
Ennistuse huvides ei lisata punkti andmeid nimistusse otseselt, vaid tehakse veidi pikem ring. Ringi
lisamise kirjeldamiseks on loodud eraldi klass LisatavRing, mis laiendab klassi AbstractUndoableEdit.
Klassis on käsud undo ja redo, kuhu tuleb kirja panna tegevused, mis tuleb sooritada vastavalt tagasi või
edasi liikumiseks. Klassis on isendimuutujaks Point (paketist java.awt) parasjagu lisatava punkti andmete
hoidmiseks. Töö lihtsustamiseks on kohe klassi LisatavRing isendi loomisel palutud tal kohe teha läbi edasi
liikumisega seotud töö ehk lisada punkti andmed. Sel juhul piisab peaprogrammis sammu tegemisel vaid
soovitud koordinaatidega LisatavaRingi loomisest. Edaspidise tagasivõtu sujumiseks tuleb vastav isend
UndoableEditEvent'i koosseisus ennistushaldurisse lisada. Nuppude abil tööjärjes edasi-tagasi liikumiseks
tuleb vaid ennistushaldurile anda käske undo või redo vastavalt soovitud suunale. Samuti on viisakas enne
kontrollida, kas vastavas suunas üldse võimalik liikuda on.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.undo.*;
import javax.swing.event.*;
import java.util.Vector;
public class Ennistus extends JApplet
implements ActionListener{
static Vector ringid=new Vector();
UndoManager ennistushaldur=new UndoManager();
Button edasi=new Button(">");
Button tagasi=new Button("<");
public Ennistus(){
getContentPane().add(tagasi, BorderLayout.WEST);
getContentPane().add(edasi, BorderLayout.EAST);
tagasi.addActionListener(this);
edasi.addActionListener(this);
addMouseListener(
new MouseAdapter(){
public void mousePressed(MouseEvent e){
ennistushaldur.undoableEditHappened(
new UndoableEditEvent(
Ennistus.this,
new LisatavRing(e.getX(), e.getY())
)
);
repaint();
}
}
);
}
public void paint(Graphics g){
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
for(int i=0; i