wiki:harjoitustyo/yksinkertainen/tiedonsyotto
Last modified 10 months ago Last modified on 2017-02-19 10:18:35

















































Ohjelmointi 2 / yksinkertaistettu esimerkki tiedonsyötöstä

Tässä mallissa itse käyttöliittymää on yksinkertaistettu ja KerhoSwing on jätetty pois ja siihen kuuluva koodi on KerhoGUI-tiedoston lopussa. Tämä lähinnä siksi, että muuten tulee ongelmia välittää hirveän monen EditPanelin tiedot toiseen tiedostoon.

Toteuteus on "tyhmä", eli vaatii paljon get ja set -metodeja Jasen-luokkaan (joita on tehty laiskuuksissaan vaan 4 paria).

Koodit ja dokumentit

Seuraavissa koodeissa rivinumerot viittaavat ko. vaiheen versioon. "Lopullisessa" versioissa siten voi olla eri rivinumerot.

Käyttöliittymä

https://svn.cc.jyu.fi/srv/svn/ohj2ht/k2014/vesal/branches/yksinkertainenVaihe7.1/kuvat/paaikkuna.png

Alkutilanne

Vaihe 6 on sellainen, missä on luokat Kerho, Jasenet ja Jasen valmiina.

Yksinkertaistetussa käyttöliittymässä KerhoGUI on tehty WindowBuilderillä toimii uuden "Aku Ankka"-jäsenen lisäys. Jäsenet näytetään ListChoose-komponenttiin ja valitun jäsenen tiedot tulostetaan yhteen JTextArea-komponenttiin.

Tietojen näyttäminen (7.1.1)

Seuraavassa vaiheessa on lisätty tietojen näyttäminen käyttöliittymään niin, että jäsenen tiedot (tai neljä ensimmäistä kenttää) vaihtuvat kun listasta valitaan toinen jäsen.

Muutokset alkutilanteeseen

Mallissa tiedot luetaan heti pääohjelman käynnistyessä eikä käyttäjä voi valita tiedoston nimeä:

55	    private static final String kerhonnimi = "kelmit";
...
93	    public static void main(final String[] args) {
94	        EventQueue.invokeLater(new Runnable() {
95	            @Override
96	            public void run() {
97	                try {
98	                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
99	                    Kerho kerho = new Kerho(); // luodaan kerho
100	                    try {
101	                    kerho.lueTiedostosta(kerhonnimi);
102	                    } catch (SailoException e) {
103	                        JOptionPane.showMessageDialog(null, "Tiedoston lukeminen: " + e.getMessage());
104	                    }
105	                    KerhoGUI frame = new KerhoGUI(kerho); // kerho dialogin käyttöön
106	                    frame.setVisible(true);
107	                } catch (Exception e) {
108	                    JOptionPane.showMessageDialog(null, "Ongelmia: " + e.getMessage());
109	                }
110	            }
111	        });
112	    }

Itse kerho viedään lomakkeelle parametrina ja näin jos olisi useita lomakkeita, ne voisivat käyttää samaa kerhoa.

KerhoGUI:n muodostajan lopussa laitetaan listalle muutoskuuntelija:

227	        listJasenet.addSelectionChangeListener(new SelectionChangeListener<Jasen>() {
228	            @Override
229	            public void selectionChange(IStringListChooser<Jasen> sender) {
230	                naytaJasen();
231	            }
232	        });
233	        alusta();
273     }

ja ihan muodostajan lopuksi kutsuttavassa alusta-metodissa on haettu tiedot listaan

308	    protected void alusta() {
309	        hae(0);  // jotta näyttää tiedostosta luetut jäsenet
310	    }
...
336	    /**
337	     * Hakee jäsenten tiedot listaan
338	     * @param jnro jäsenen numero, joka aktivoidaan haun jälkeen
339	     */
340	    protected void hae(int jnro) {
341	        listJasenet.clear();
342	
343	        int index = 0;
344	        for (int i = 0; i < kerho.getJasenia(); i++) {
345	            Jasen jasen = kerho.annaJasen(i);
346	            if (jasen.getTunnusNro() == jnro) index = i;
347	            listJasenet.add(jasen.getNimi(), jasen);
348	        }
349	
350	        listJasenet.setSelectedIndex(index); // täsät tulee muutosviesti joka näyttää jäsenen
351	    }

Jäsenen tietojen näyttäminen tapahtuu siis kun valitun indeksi muuttuu (listan valinnan vaihtaminen laitettiin kutsumaan naytaJasen:

313	    /**
314	     * Näyttää jäsenen tiedot jäsenen alueeseen
315	     */
316	    private void laitaJasen() {
317	        if (jasenKohdalla == null) return;
318	        editPanelNimi.setText(jasenKohdalla.getNimi());
319	        editPanelHetu.setText(jasenKohdalla.getHetu());
320	        editPanelKatuosoite.setText(jasenKohdalla.getKatuosoite());
321	        editPanelPostinumero.setText(jasenKohdalla.getPostinumero());
322	    }
323	   
324	
325	    /**
326	     * Näyttää listasta valitun jäsenen tiedot, tilapäisesti yhteen isoon edit-kenttään
327	     */
328	    protected void naytaJasen() {
329	        int ind = listJasenet.getSelectedIndex();
330	        if (ind < 0) return;
331	        jasenKohdalla = listJasenet.getSelectedObject();
332	        laitaJasen();
333	    }

Jasen-luokkaan on täytynyt lisätä get-metodeja, jotta sieltä saadaan tietoa EditPanel-kenttiin.

Nyt jos tiedostoon kelmit.dat laittaa käsin esimerkkidataa, niin se näkyy käyttöliitymässä ja jäseniä voi selata. Tai jäseniä voidaan lisätä myös Lisää jäsen -painikkeella, jolloin lisääntyy "Aku Ankka"-jäseniä.

Tiedon siirto lomakkeelta jäseneen (7.1.2)

Seuraavaksi on lisätty koodia, jotta tiedot saadaan siirrettyä lomakkeelta jäseneen. Varsinaiseen toteutukseen verrattuna erona on se, että muutos laitetaan heti oikeaan jäsenistöön ja sitä ei voi perua. Samoin uusi tyhjä jäsen lisätään jäsenistöön ja näin voi helposti syntyä joukko tyhjiä jäseniä, jos käyttäjät ei syötä uudelle jäsenelle tietoja. Jäsenen poistamista ei vielä ole.

Muutokset näyttämiseen verrattuna

Tietojen muuttumista varten täytyy EditPaneleille laittaa kuuntelijat KerhoGUI:n muodostajassa:

235	        editPanelNimi.addKeyListener(new KeyAdapter() {
236	            @SuppressWarnings("synthetic-access")
237	            @Override
238	            public void keyReleased(KeyEvent e) {
239	                kasitteleMuutosJaseneen(1,editPanelNimi);
240	            }
241	        });
242	       
243	        editPanelHetu.addKeyListener(new KeyAdapter() {
244	            @SuppressWarnings("synthetic-access")
245	            @Override
246	            public void keyReleased(KeyEvent e) {
247	                kasitteleMuutosJaseneen(2,editPanelHetu);
248	            }
249	        });
250	       
251	        editPanelKatuosoite.addKeyListener(new KeyAdapter() {
252	            @SuppressWarnings("synthetic-access")
253	            @Override
254	            public void keyReleased(KeyEvent e) {
255	                kasitteleMuutosJaseneen(3,editPanelKatuosoite);
256	            }
257	        });
258	       
259	        editPanelPostinumero.addKeyListener(new KeyAdapter() {
260	            @SuppressWarnings("synthetic-access")
261	            @Override
262	            public void keyReleased(KeyEvent e) {
263	                kasitteleMuutosJaseneen(4,editPanelPostinumero);
264	            }
265	        });
266	       
267	        alusta();
268	    }

Tässä muuten syy, miksi mallissa käsitellään vain neljää kenttää. Kukaan ei jaksa tehdä tuota useammalle. Oikeassa versiossahan kentät on indeksoitu, jolloin homma helpottuu huomattavasti. Tässäkin jokaisen EditPanelin tekstimuutos on laitettu menemään samaan käsittelijään kasitteleMuutosJaseneen, jolle viedään parametrina muuttuneen kentän indeksi sekä paneli, jossa muutos tapahtui. Muuten olisi jouduttu kirjoittamaan neljä lähes identtistä käsittelijää.

391	     * Käsitellään jäseneen tullut muutos
392	     * @param k mitä kenttää muutos koskee
393	     * @param edit muuttunut kenttä
394	     */
395	    protected void kasitteleMuutosJaseneen(int k, EditPanel edit) {
396	        if (jasenKohdalla == null) return;
397	        String s = edit.getText();
398	        String virhe = null;
399	        switch (k) {
400	           case 1 : virhe = jasenKohdalla.setNimi(s); break;
401	           case 2 : virhe = jasenKohdalla.setHetu(s); break;
402	           case 3 : virhe = jasenKohdalla.setKatuosoite(s); break;
403	           case 4 : virhe = jasenKohdalla.setPostinumero(s); break;
404	           default:
405	        }
406	        if (virhe == null) {
407	            edit.setToolTipText("");
408	            edit.setBackground(normaaliVari);
409	            kerho.setMuutosJaseneen();
410	        } else {
411	            edit.setToolTipText(virhe);
412	            edit.setBackground(virheVari);
413	        }
414	       
415	        if ( k == 1 ) { // jos nimeä muutettu, muutetaan myös listaa samalla
416	            hae(jasenKohdalla.getTunnusNro());  // virkistää listan
417	        }
418	    }

Tässä käsittelijässä otetaan ensin talteen paneeliin kirjoitettu uusi teksti. Kentästä riippuen katsotaan voiko sen sijoittaa jäsenelle. Tämä on koodin huonoiten ylläpidettävä osa ja tuo toimii paremmin kun kentät on indeksoitu.

Jasen-luokaan on lisätty set-metodeja, jotka palauttava null jos asettaminen onnistuu ja muuten virhetiedon merkkijonona.

Jos tuli virhe, niin kentän väri vaihdetaan ja laitetaan virhetieto ToolTippiin. Jos ei, niin ilmoitetaan kerholle, että jäsenen tiedot ovat muuttuneet, jolloin seuraava tallennus suostuu tiedot tallentamaan.

Vikana tässä on vielä se, että jos kenttään kirjoittaa laitonta tekstiä, jää punainen näkyviin myös kun siirrytään toiseen jäseneen.

Jos muutetaan nimi-kenttää, niin myös lista pitää saada nimen osalta ajantasalle. Helpoiten tämä onnistuu muodostamalla lista uudelleen. Tässä toteutuksessa on se ongelma, että koska listan hakeminen uudelleen vaihtaa Edit-kentissä olevan tiedon, hyppää kursori aina editPanelNimi-kentän loppuun ja nimeä ei voi siten editoida muuta kuin lopusta. Eli tähän käyttöön valittu KeyReleased-tapahtuma ei ole hyvä. Korjauksena voisi olla se, että editPanelNimi-kenttään laitettaisiin ActionPerformed ja kentästä poistumistapahtuma ja niillä vasta vaihdettaisiin ListBoxin sisältöä.

Jos lisätään uusi jäsen, niin luodaan se, rekisteröidään, lisätään kerhoon ja laitetaan se muokattavaksi:

421	    /**
422	     * Luo uuden jäsenen jota aletaan editoimaan
423	     */
424	    protected void uusiJasen() {
425	        Jasen uusi = new Jasen();
426	        uusi.rekisteroi();
427	        try {
428	            kerho.lisaa(uusi);
429	        } catch (SailoException e) {
430	            JOptionPane.showMessageDialog(null, "Ongelmia uuden luomisessa " + e.getMessage());
431	            return;
432	        }
433	        // hae(uusi.getTunnusNro()); // tämä olis muuten hyvä, mutta listan rivi jäisi tyhjäksi
434	        listJasenet.add("Uusi",uusi);
435	        int n = listJasenet.count();
436	        listJasenet.setSelectedIndex(n-1);  // aiheuttaa muutostapahtuman ja siis naytaJasen
437	    }

Kuuntelijalistan sieventäminen (7.1)

KerhoGUI:n kuuntelijalistan tekemistä saa hieman sivennettyä poistamalla kuuntekijoiden lisääminen muodostajasta:

236	        alusta();
237	    }

Sitten tehdään oma kuuntelijaluokka:

309	    /**
310	     * Oma luokka kuuntelemaan editin tapahtumia
311	     */
312	    protected class EditKeyListener extends KeyAdapter {
313	        private int k;
314	        /**
315	         * @param k mitä kenttää muutos koskee
316	         */
317	        public EditKeyListener(int k) { this.k = k; }
318	        @Override
319	        public void keyReleased(KeyEvent e) {
320	            kasitteleMuutosJaseneen(k,(JTextField)e.getSource());
321	        }
322	    }

joka tallettaa kentän indeksin itseensä ja tapahtuman tullessa kutsuu varsinaista käsittelijää kentän indeksillä (k). "Oikeassa" versiossahan tämä on hoidettu yhdellä kuuntelijalla ja kuhunkin edittiin on sen nimeen haudattu kentän indeksi. Tosin "oikeassakin" versiossa olisi voitu käyttää tätä samaa ideaa varsin sujuvasti.

Koska e.getSource() on nimenomaan EditPanelin sisällä oleva JTextField, joudutaan hieman muuttamaan itse käsittelijääkin ja vaihtamaan parametri JTextField-tyyppiseksi:

384	    protected void kasitteleMuutosJaseneen(int k, JTextField edit) {
385	        if (jasenKohdalla == null) return;
386	        String s = edit.getText();
387	        String virhe = null;
...	 

Itse kuuntelijat laitetaan paikalleen alusta-metodissa:

 /**
326	     * Tekee tarvittavat muut alustukset
327	     * Lisätään kuuntelijat edit-kenttiin
328	     */
329	    protected void alusta() {
330	        editPanelNimi.addKeyListener(new EditKeyListener(1));
331	        editPanelHetu.addKeyListener(new EditKeyListener(2));
332	        editPanelKatuosoite.addKeyListener(new EditKeyListener(3));
333	        editPanelPostinumero.addKeyListener(new EditKeyListener(4));
334	        hae(0);  // jotta näyttää tiedostosta luetut jäsenet
335	    }