Tarkoitus olisi tehdä ohjelmaan käyttöliittymä joka toimisi pääasiassa yhdessä ikkunassa. Tätä varten täytyisi kuitenkin osata järjestellä komponentteja uudelleen.
Jos oletetaan, että ohjelman ikkunan oikealla puolella on napit jotka muuttavat vasemmalla puolella olevaa tilaa. Esim tietojen lisaystä ja hakua. Niin miten tämä kuuluisi toteuttaa? En onnistunut löytää yhtään aihetta sivuavaa tutoriaalia netistä, tosin en myöskään tarkkaan tiedä mitä pitäisi etsiä.
Toivottavasti ei ollut liian sekavaa tekstiä.
Jos koko näkymä vaihtuu, kannattaa ehkä käyttää eri näkymille aivan omia komponentteja ja piilottaa ja näyttää näitä. Pienimmällä vaivalla tietenkin pääsee, kun sijoittaa näkymät omiin Containereihin, jolloin riittää näyttää ja piilottaa oikeat näistä. Jos taas muutokset ovat pieniä, sijaintia ja kokoa voi muuttaa setLocation- ja setSize- ja setBounds-metodeilla.
Muitakin eri tarkoituksiin sopivia apuluokkia on, kunhan kerrot, mitä tarkemmin ottaen haluat saada aikaan.
CardLayout saattaisi olla tarkoitukseesi sopiva.
How to Use CardLayout
Tuo CardLayout vaikuttaa juuri siltä mitä haen. En kuitenkaan saa sitä ihan toimimaan. Luultavasti huonon syntaksin ymmärrykseni takia.
Yritin demota tuota yksinkertaiseen BorderLayout asettelumallia käyttävään ohjelmaan:
package testi; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Testi extends JFrame { /*Luodaan containerit komponenteille. korrti1 ja -2 menevät kortit paneeliin * joka vuorostaan sijoitetaan pohja-paneeliin. Myös napit-paneeli upotetaan pohja-paneeliin*/ private JPanel pohja = new JPanel(new CardLayout()); private JPanel kortit = new JPanel(); private JPanel napit = new JPanel(); private JPanel kortti1 = new JPanel(); private JPanel kortti2 = new JPanel(); //Luodaan kaikki tarvittavat komponentit konstruktoriin //korttiNapit vaihtavat korttinäkymää private JButton korttiNappi1 = new JButton("Kortti1"); private JButton korttiNappi2 = new JButton("Kortti2"); private JButton nappi1 = new JButton("nappi"); private JButton nappi2 = new JButton("toinen nappi"); private JLabel selite = new JLabel("Selite"); private JTextField tekstikentta = new JTextField(20); public Testi() { GroupLayout asettelu = new GroupLayout(napit); napit.setLayout(asettelu); asettelu.setAutoCreateGaps(true); asettelu.setAutoCreateContainerGaps(true); //X GroupLayout.ParallelGroup pohjaX = asettelu.createParallelGroup(); pohjaX.addComponent(korttiNappi1); pohjaX.addComponent(korttiNappi2); asettelu.setHorizontalGroup(pohjaX); //Y GroupLayout.SequentialGroup pohjaY = asettelu.createSequentialGroup(); pohjaY.addComponent(korttiNappi1); pohjaY.addComponent(korttiNappi2); asettelu.setVerticalGroup(pohjaY); //uusi paneeli GroupLayout asettelu2 = new GroupLayout(kortti1); kortti1.setLayout(asettelu2); asettelu2.setAutoCreateGaps(true); asettelu.setAutoCreateContainerGaps(true); //X GroupLayout.SequentialGroup kortti1X = asettelu2.createSequentialGroup(); kortti1X.addComponent(korttiNappi1); kortti1X.addComponent(korttiNappi2); asettelu2.setHorizontalGroup(kortti1X); //Y GroupLayout.ParallelGroup kortti1Y = asettelu2.createParallelGroup(); kortti1Y.addComponent(korttiNappi1); kortti1Y.addComponent(korttiNappi2); asettelu2.setVerticalGroup(kortti1Y); //uusi paneeli GroupLayout asettelu3 = new GroupLayout(kortti2); kortti2.setLayout(asettelu3); asettelu3.setAutoCreateContainerGaps(true); asettelu3.setAutoCreateGaps(true); //X GroupLayout.ParallelGroup kortti2X = asettelu3.createParallelGroup(); kortti2X.addComponent(selite); kortti2X.addComponent(tekstikentta); asettelu3.setHorizontalGroup(kortti2X); //Y GroupLayout.SequentialGroup kortti2Y = asettelu.createSequentialGroup(); kortti2Y.addComponent(selite); kortti2Y.addComponent(tekstikentta); asettelu3.setVerticalGroup(kortti2Y); korttiNappi1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { vaihdaKorttia(); } }); korttiNappi2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { vaihdaKorttia(); } }); // Kortit pitäisi vielä nimetä kortit.add(kortti1); kortit.add(kortti2); pohja.add(napit, BorderLayout.PAGE_START); pohja.add(kortit, BorderLayout.CENTER); this.add(pohja); this.pack(); this.setTitle("CardLayout testi"); this.setLocation(400,400); this.setDefaultCloseOperation(EXIT_ON_CLOSE); } private void vaihdaKorttia() { //Mitä tähän? } public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { new Testi().setVisible(true); } }); } }
Tuossa minua askarruttaa korttien nimeäminen. Miten muuten ne voi nimetä kuin Sunin tutoriaalissa mallissa String muuttujina? Se varmaan on ihan kätevä tapa kun kortteja vaihdellaan comboBoxilla, mutta ei kun niitä muutetaan napeilla(kai?).
Sitten toinen asia on vaihdaKorttia()-metodin sisältö. Mallissa käytetään ItemEvent-luokkaa. Mutta koska itse käytän nappeja. Niin minun varmaan pitäisi silloin käyttää ActionEvent-luokkaa ja getActionCommand() metodia? Tuosta metodista en ole ihan varma, mutta se oli lähinnä mitä onnistuin löytämään. Sitten vielä toivottavasti muussa koodissa ei olisi mitään aivopieruja. Harvemmin tulee säädettyä noin monella asettelumallilla samassa tiedostossa.
Ensiksi tuolle kortit-paneelille pitää tietysti asettaa oikea layout:
CardLayout korttiLayout = new CardLayout(); kortit.setLayout(korttiLayout);
Sitten korttien nimeäminen pitäisi olla melko suoraviivaista (nimet voi ja luultavasti kannattaakin asettaa vakioiksi tai tehdä niistä enum, jolloin mm. typojen mahdollisuus pienenee ja nimeä vaihdettaessa se tarvitsee muuttaa vain yhteen paikkaan):
kortit.add(kortti1, "yks"); kortit.add(kortti2, "kaks");
Ja sitten se kortin vaihtaminen: joko muutat vaihdaKorttia metodin ottamaan parametriksi kortin johon vaihdetaan tai sitten teet vaihdon suoraan noissa actionPerformed-metodeissa. Esim.
korttiNappi1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { vaihdaKorttia("yks"); } }); private void vaihdaKorttia(String kortti) { korttiLayout.show(kortti); }
Lisäksi tuo koodi luultavasti selkenisi melko paljon, jos pilkot sitä vielä pienempiin osiin. Esimerkiksi jokaisesta kortista voi tehdä vaikka oman luokan, ainakin jos niihin kortteihin on tulossa paljon tavaraa.
No ei tää kyl vieläkään toimi :)
Nimesin kortit ja määrittelin kortit-paneelille oikean asettelumallin juuri ennen korttien lisäämistä paneeliin. Ongelma tuleekin vaihdaKorttia() metodissa. Heittää nimittäin "Can not find symbol" virheen korttiLayoutista ja show()-metodista.
Eli jos oikein tätä tulkitsen. Niin kääntäjä ei löydä luomaani korttiLayouttia, eikä tämän takia myöskään tunnista show() metodia.
Dokumentaatio tietää, että show-metodi ottaa kaksi parametria.
Jos tämänkään jälkeen ei toimi, tarkista, että korttiLayout on oikeassa paikassa. Jos käytät sitä useammassa metodissa, sen täytyy tietenkin olla luokan jäsen eikä vain paikallinen muuttuja kuten Samin esimerkissä.
Kortit-paneelin asettelu:
CardLayout korttiLayout = new CardLayout(); kortit.setLayout(korttiLayout); kortit.add(kortti1,"yks"); kortit.add(kortti2,"kaks");
vaihdaKorttia() metodi:
private void vaihdaKorttia(String kortti) { korttiLayout.show(kortit, kortti); }
Sama ongelma vieläkin. En ihan ymmärrä tuota "tarkista, että korttiLayout on oikeassa paikassa. Jos käytät sitä useammassa metodissa, sen täytyy tietenkin olla luokan jäsen eikä vain paikallinen muuttuja kuten Samin esimerkissä."
Mikä siitä tekee nyt paikallisen muuttujan? Miten muutan sen luokan jäseneksi?
Sori että tää nyt menee näin kädestä taluttamiseksi :>
Paikallinen muuttuja on sellainen, joka esitellään funktion sisällä, luokan jäsenet taas esitellään luokan sisällä mutta funktioiden ulkopuolella. Paikallisia muuttujia voi käyttää niiden esittelystä lohkon sulkevaan aaltosulkuun asti.
class luokka { public int jasen; public void funktio() { // Jäsenmuuttujaa voi käyttää: jasen = 1; // Tässä muuttujaa p ei ole vielä määritelty. // p = 2; olisi siis virhe, koska sitä ei ole vielä. int p; // Nyt se on määritelty ja sitä voi käyttää. p = 10; } // Täällä muuttuja ei ole enää voimassa. // Sen olemassaolo päättyi funktion lopettavaan aaltosulkuun. public void f2() { // Täälläkään muuttuja p ei ole olemassa, koska se kuuluu eri funktioon. // Sen sijaan luokan jäsen säilyy. if (jasen == 1) {} // ... } }
No nyt tein tällaiset muutokse:
public class Testi Extends JFrame { ... CardLayout korttiLayout; ...
ja
public Testi() { ... kortit.setLayout(korttiLayout); kortit.add(kortti1,"yks"); kortit.add(kortti2,"kaks"); ... }
Mutta nyt kun ohjelmaa yrittää suorittaa, niin tuleekin virhe joka kertoo, että komponentin asettelussa olisi tapahtunut jokin virhe.
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: javax.swing.JLabel[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,border=, flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultIcon=, disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4, labelFor=,text=Selite,verticalAlignment=CENTER,verticalTextPosition=CENTER] is not attached to a horizontal group
Eli luultavasti selite textFieldiä ei ole asetettu oikein.
Tässä vielä tuota käsittelevä koodi. Itse en ainakaan löydä tuosta mitään vikaa. Tosin eipä olisi ensimmäinen kerta kun jotain jäisi huomaamatta oikoluvusta huolimatta.
GroupLayout asettelu3 = new GroupLayout(kortti2); kortti2.setLayout(asettelu3); asettelu3.setAutoCreateContainerGaps(true); asettelu3.setAutoCreateGaps(true); //X GroupLayout.ParallelGroup kortti2X = asettelu3.createParallelGroup(); kortti2X.addComponent(selite); kortti2X.addComponent(tekstikentta); asettelu3.setHorizontalGroup(kortti2X); //Y GroupLayout.SequentialGroup kortti2Y = asettelu.createSequentialGroup(); kortti2Y.addComponent(selite); kortti2Y.addComponent(tekstikentta); asettelu3.setVerticalGroup(kortti2Y);
Palataanpa taas aiheeseen.
Nyt yritän tehdä tätä samaa, mutta laittamalla jokaisen kortin omaan luokkaansa. Jos minulla olisi siis luokat kortti1 ja kortti2, ja niissä saman nimiset metodit jotka rakentavat halutun näkymän. Tällöin näiden korttien JPaneelien varmaankin täytyisi olla public jotta pääohjelmalla olisi mahdollisuus lukea sitä? Entä miten näitä kortti-metodeja olisi tarkoitus kutsua, jotta pääohjelma osaisi piirtää niissä olevat paneelit pääikkunaan?
Mikset tee siitä luokasta esimerkiksi JPanelin aliluokkaa?
public class Kortti1 extends JPanel { //... }
Ja sitten vaan luot siitä ilmentymän, jonka laitat kortiksi.
//... Kortti1 kortti1 = new Kortti1(); kortit.add(kortti1,"yks"); //...
En oikein hallitse tätä datan liikuttelua luokasta toiseen. Minulla on ohjelma jonka reunassa on välilehti. Välilehdissä on nappeja jotka vaihtavat ohjelman muuta näkymää. Kun yhden tabissa olevan napin ActionListener() kutsuu vaihdaKortteja()-metodia. Niin miten anann tämän käskys pääikkunalle jossa kaikki kortit kasataan yhteen? Jokainen kortti on siis omassa luokassaan.
Aihe on jo aika vanha, joten et voi enää vastata siihen.