The page has been modified since the last reload. Refresh now?

There are {{ $ctrl.pendingUpdatesCount() }} pending paragraph updates.

{"classes": ["linkit"], "rd": "114039", "rl": "no", "rp": "jjgCelf0svvh"}

StringGrid

StringGrid on TableView-komponentista peritty komponentti, jonka tarkoitus on helpottaa taulukkomuotoisen aineiston käsittelyä.

Merkittävä ero TableView-komponenttiin on se, että alkioihin pääsee helposti käsiksi rivi- ja sarakeindeksin avulla. Rivi- ja sarakeindeksi ovat indeksejä alkueräiseen lajittelemattomaan taulukkoon nähden. Lisäksi kunkin solun ulkoasua voi säätää helposti erikseen.

Jar-tiedoston hakeminen: fxgui.jar.

1. Käyttö suunnitteluaikana

Ohjelman ulkoasun suunnitteluaikana voi StringGrid-komponenttiin lisätä malliaineistoa laittamalla Rivit-ominaisuuteen tekstiä tyyliin:

ala|aloitusvuosi|h/vko
kalastus|1955|20
laiskottelu|1950|20
työn pakoilu|1952|40

Katso tarkemmin SceneBuilderin esimerkistä.

2. Yleisimpiä toimintoja

Kaikki (tai ainakin suurin osa) TableView-komponentin ominaisuuksista toimii.

Merkintä (SC) alla tarkoittaa että ominaisuuden voi valita myös SceneBuilderissä. Esimerkeissä taulukon nimi vaihtelee sen mukaan mistä ohjelmasta koodinpala on kopioitu.

2.1 Sarakeiden luominen

Mikäli sarakemäärä on saatu oikeaksi ja otsikot ovat oikeita jo suunnitteluaikana, ei tässä kohti tarvitse tehdä muuta.

Mikäli sarakemäärää halutaan muuttaa, täytyy koko talukko luoda (toistaiseki) uudelleen.

Luodaan esimerkiksi RistiNollaa varten sarakkeet, joissa 1. otsikko on tyhjä ja muissa juokseva numero:

        String[] headings = new String[koko+1];
        headings[0] = "";
        for (int i=1; i<=koko; i++) headings[i] = ""+i;
        grid.initTable(headings); 

Tämä tyhjentää kaikki asetukset, eli on tehtävä ennen muita rivi-/sarakeasetuksia.

Koska metodi initTable on esitelty vapaan parametrilistan muodossa:

public void initTable(String... headings) {

voidaan sarakkeet alustaa myös antamalla pilkkueroteltu lista otsikoita:

grid.initTable("nimi","syntymävuosi","osoite"); 
#

Please to interact with this component

2.2 Yleisiä asetuksia

Järjestetään sarake 1 numeerisesti:

        tableHarrastukset.setColumnSortOrderNumber(1);

Sarakkeen 2 leveys:

        tableHarrastukset.setColumnWidth(2, 60);

Kaikkien sarakkeiden leveys kerralla:

        grid.setColumnWidth(-1,30);   

Estetään kaikkien sarakkeiden lajittelu (vastaavasti yksittäisen sarakkeen kun laitetaan vastaava sarakeindeksi):

grid.setSortable(-1, false); 

Estetään sarakkeiden järjestyksen vaihtaminen:

grid.disableColumnReOrder();

Muokkaus päälle/pois (SC):

        tableHarrastukset.setEditable(false);

Yksittäisiä soluja voidaan valita koko rivin sijaan

        grid.getSelectionModel().setCellSelectionEnabled(true); 

Menu, josta voi valita sarakkeet

grid.setTableMenuButtonVisible(true); 
#

Please to interact with this component

2.3 Rivien lisääminen

Rivin lisääminen ilman riviin kuuluvata olioita joko

        grid.add("Matti", ""+vuosi);

tai

        String[] rivi = {"Matti","1970");
        grid.add(rivi);
#

Please to interact with this component

Alkion ja sen näkyvien tietojen lisääminen:

    private void naytaHarrastus(Harrastus har) {
        int kenttia = har.getKenttia();
        String[] rivi = new String[kenttia-har.ekaKentta()];
        for (int i=0, k=har.ekaKentta(); k < kenttia; i++, k++)
            rivi[i] = har.anna(k);
        tableHarrastukset.add(har,rivi);
    }

Tällöin itse taulukko pitää olla esiteltynä tallentamaan vastaavia olioita:

    @FXML private StringGrid<Harrastus> tableHarrastukset;
#

Please to interact with this component

2.3.1 Pelkkien olioiden lisääminen

Voidaan lisätä myös olioita ilman vastaavia merkkijonoja:

grid.add(jasenet);

Tällöin ei enää toimi grid.get(r,c). Ja jotta mitään näkyisi, pitää taulukolle kertoa mistä saadaan kutakin alkiota (rivi, r, sarake c) kohti olion vastaavan kentän sisältö merkkijonona.

Tämä tehdään lisäämällä tapahtumankäsittelijolio, joka palautta tiedon siitä, mitä sisältöä soluun pitää näyttää. Usein tässä oikaistaan tekemällä tätä varten lambda-lauseke tyyliin:

grid.setOnCellString( (g, jasen, defValue, r, c) -> jasen.anna(c+eka) );

Tätä funktiota StringGrid-olio kutsuu jokaiselle taulukon solulle eriksen. Solun näyttämistapahtumalle tulee siis parametrina (edellisillä nimillä):

  • g = grid jonka solun arvo tarvitaan
  • jasen = olio joka liitetty ko riville
  • defValue = oletus solun sisällölle
  • r = row, eli rivin indeksi, solulle jonka sisältö halutaan
  • c = column, eli sarakkeen indeksi solule, jonka sisältö halutaan

Aina tapahtuman käsitelyssä ei tarvitse käyttää kaikkia parametreja. Esimerkiksi jos tieto saadaan oliosta itsestään, kuten edellä, riittää pyytää oliolta tieto, että mikä arvo laitetaan oliota vastaavan rivin soluun paikassa c.

Olioita voi lisätä kerrallaan yhden tai kokonaisen tietorakenteen:

#

Please to interact with this component

Mikäli tämän lisäksi halutaan lajitella sarakkeita muuhun järjestykseen kuin merkkijonojärjestykseen (esimerkiksi hetujen vuoden perusteella), täytyy myös sanoa miten saadaan lajittelussa käytettävä merkkijono. Numeerisen järjestyksen saa sillä että ilmoittaa sarakkeen numeeriseksi.
Esimerkiksi:

 grid.setOnCellValue( (g, jasen, defValue, r, c) -> jasen.getAvain(c+eka) );

2.4 Solun arvon muuttaminen ja katsominen

Solun tekstiä voidaan vaihtaa:

        grid.set(jono, r, c);

Vastaavasti solussa oleva teksti voidaan hakea:

        String jono = grid.get(r, c);

Otsikko-solun arvoa voidaan muuttaa:

        grid.getColumns().get(0).setText(uusiOtsikko); // muutetaan saraketta 0

2.5 Tyhjät solut

Piilotetaan ylimääräinen sarake oikealta (SC)

    grid.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

Rivien alle jäävien tyhjien solujen tyyli (SC):

    grid.setEmptyStyleClass("tyhja")

Tämä sama voidaan tehdä myös suoraan .css-tiedoston avulla laittamalla sinne tyyliin:

.table-row-cell:empty {
   -fx-background-color: white;
}

.table-row-cell:empty .table-cell { 
   -fx-border-width:     0px;
}

2.6 Solujen värit

Solujen väreihin voidaan vaikuttaa .css-tyyleillä. Halutut tyylit tehdään .css-tiedostoon ja itse koodiin voidaan sitten laittaa esimerkiksi:

        grid.setStyleClass("virhe,sininen", 1, 1);
        grid.setStyleClass("s1", 0, 0);
        grid.setStyleClass("s2", 1, 0);
        grid.setStyleClass("s3", 2, 0);

Rivi- ja sarakeindeksit ovat solujen alkuperäisen sijainnin mukaan riippumatta onko rivejä tai sarakkeita järjestelty uudelleen.

Mikäli solujen värejä muutetaan ja rivejä järjestellään, tulee tästä ongelmia, jotka voidaan kiertää pakottamalla taulukon uudelleenpiirto mikäli otsikoihin kosketaan:

        grid.setOnMouseClicked( e -> {
            int r = grid.getRowNr();
            int c = grid.getColumnNr();
            if ( r < 0 || c < 0 ) grid.refresh();
          });

2.7 Solun klikkauksen kuunteleminen

Solun klikkauksen kuuntelemiseksi lisätään kuuntelija:

        grid.setOnMouseClicked( e -> {...} );

Tapahtumasta e voi kysyä klikkausten lukumäärän:

        if (  e.getClickCount() == 2) 

Klikatun solun koordinaatin (alkuperäisinä indekseinä) saa selville

            int r = grid.getRowNr();
            int c = grid.getColumnNr();

Vastaavasti riviä vastaavan olion saa tarvittaessa selville:

    Harrastus har = tableHarrastukset.getObject(r);

2.8 Näppäinten kuunteleminen

Vastaavasti näppäimiä voidaan kuunnella:

        grid.setOnKeyPressed( e -> {if ( e.getCode() == KeyCode.ENTER ) laitaMerkki();});

2.9 Solujen muokkaus

Jotta soluja voidaan muokata “lennossa”, pitää tämä ilmoittaa taulukolle:

grid.setEditable(true);

Sitten mikäli halutaan muokatulle solulle tehdä jotakin tarkistuksia niin pitää tehdä takaisinkutsu tyyliin:

        grid.setOnGridLiveEdit((g, jasen, defValue, r, c, edit) -> {
            String virhe = jasen.aseta(c+eka,defValue);
            if ( virhe == null ) {
                kerho.korvaaTaiLisaa(jasen); // jotta saadaan muutos
                edit.setStyle(null);
                Dialogs.setToolTipText(edit,"");
            } else {
                edit.setStyle("-fx-background-color: red");
                Dialogs.setToolTipText(edit,virhe);
            }
            return defValue;
        });

3. Käyttöesimerkkejä

Seuraavana muutamia esimerkkiohjelmia tai niiden osia, joissa on käytetty StringGridin ominaisuuksia.

Kaikki esimerkit saa ehkä helpoiten Eclipse-käyttöön ottamalla svn export osoitteesta:

https://svn.cc.jyu.fi/srv/svn/ohj2/FXExamples/trunk/Examples/

3.1 RistiNolla

Ristinolla on yksinkertainen esimerkki RistiNolla.pelistä, missä solua klikkaamalla siiten tulee vuoroin X ja vuoroin O. Tässä myös eri solun väri vaihtuu. Vuorossa olevan pelaajan merkki näkyy taulukon vasemassa yläkulmassa.

Pelaaminen valmiista .jar-tiedostosta:

Ajettava esimerkki

#

3.2 Nimi ja vuosi

Esimerkki uudemman StringGridin käytöstä. Tässä on vain tarkoitus esitellä eri ominaisuuksia. Ominaisuuksia pitää katsoa yksittäisinä eikä niitä ole kaikkia pakko käyttää.

Kun muokataan numerosolua, niin se tulee punaiseksi jos jono ei vastaa kokonaislukua. Esimerkissä on aluksi asetettu yksi solu muuten vaan punaiseksi. Kun soluja klikataan, niistä lähtee väri pois. Muokatun solun jälkeen näytetään oikeassa reunassa muokatun solun arvo ja koordinaatti.

Ajaminen valmiista .jar-tiedostosta:

java -cp Examples.jar stringgrid.NimiJaVuosiMain

Ajettava esimerkki

3.3 Kerhon harrastukset

Alla tärkeimmät muutokset harrastusten näyttämiseksi. Esimerkissä kunkin harrastuksen tiedot laitetaan ensin merkkijonotaulukkoon, joka lisätään StringGridin riviksi (metodi naytaHarrastus).

public class KerhoGUIController implements Initializable {
    ...
    @FXML private StringGrid<Harrastus> tableHarrastukset;
    ...
    protected void alusta() {
        ...
        tableHarrastukset.setPlaceholder(new Label("Ei vielä harrastuksia"));
        
        // alustetaan hrrustustaulukon otsikot
        int eka = apuharrastus.ekaKentta();
        int lkm = apuharrastus.getKenttia();
        String[] headings = new String[lkm-eka];
        for (int i=0, k=eka; k<lkm; i++, k++) headings[i] = apuharrastus.getKysymys(k);
        tableHarrastukset.initTable(headings);
        tableHarrastukset.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        tableHarrastukset.setEditable(false);
        
        // Tämä on vielä huono, ei automaattisesti muutu jos kenttiä muutetaan.
        tableHarrastukset.setColumnSortOrderNumber(1);
        tableHarrastukset.setColumnSortOrderNumber(2);
        tableHarrastukset.setColumnWidth(1, 60);
        tableHarrastukset.setColumnWidth(2, 60);
    }

    ...

    private void naytaHarrastukset(Jasen jasen) {
        tableHarrastukset.clear();
        if ( jasen == null ) return;
        
        try {
            List<Harrastus> harrastukset = kerho.annaHarrastukset(jasen);
            if ( harrastukset.size() == 0 ) return;
            for (Harrastus har: harrastukset)
                naytaHarrastus(har);
        } catch (SailoException e) {
            // naytaVirhe(e.getMessage());
        } 
    }


    private void naytaHarrastus(Harrastus har) {
        int kenttia = har.getKenttia();
        String[] rivi = new String[kenttia-har.ekaKentta()];
        for (int i=0, k=har.ekaKentta(); k < kenttia; i++, k++)
            rivi[i] = har.anna(k);
        tableHarrastukset.add(har,rivi);
    }
    ...
    
    private void naytaJasen() {
        jasenKohdalla = listJasenet.getSelectionModel().getSelectedItem();
        if (jasenKohdalla == null) return;
        JasenDialogController.naytaJasen(edits, jasenKohdalla);
        naytaHarrastukset(jasenKohdalla);
    }
    ...
}    
#
#

3.4 Esimerkki: Jäsenen muokkaus StringGridissä

Tässä esimerkissä on tehty kokonainen ohjelma jossa on esimerkki jäsenten muokkaamiseksi StringGridissä. Esimerkissä ei lisätä merkkijonoja taulukon avulla kuten harrastusesimerkissä, vaan kysytään aina jäseneltä mikä teksti näytetään mihinkäkin soluun. Samoin lajittelua varten kerrotaan millaisen jonon perusteella lajitellaan. Esimerkin koodi on hieman lyhentynyt kun SailoException on peritty ajonaikaisesta poikkeuksesta ja näin sitä ei ole pakko ottaa kiinni joka paikassa:

public class SailoException extends RuntimeException {
Esimerkki jäsenten muokkauksesta taulukossa
Esimerkki jäsenten muokkauksesta taulukossa

Esimerkissä voi muokata jäseniä, mutta ei poistaa eikä lisätä. Mikäli muokkausmahdollisuus poistettaisiin, lyhenisi alusta-metodi melkein puolella (alusta voisi olla myös staattinen).

#

JasenStringGrid.java

018 public class JasenStringGrid extends Application {
019     @Override
020     public void start(Stage primaryStage) {
021         try {
022             final BorderPane root = new BorderPane();
023             StringGrid<Jasen> grid = new StringGrid<>();
024             root.setCenter(grid);
025             
026             primaryStage.setScene(new Scene(root));
027             primaryStage.setTitle("Kerho");
028             
029             Kerho kerho = new Kerho();
030             kerho.lueTiedostosta("kelmit");
031             alusta(kerho, kerho.etsi(null, 0),grid);
032             
033             primaryStage.setOnCloseRequest((event) -> kerho.tallenna() );
034 
035             primaryStage.show();
036         } catch(Exception e) {
037             e.printStackTrace();
038         }   
039     }
040 
041     
042     private void alusta(Kerho kerho, Collection<Jasen> jasenet, StringGrid<Jasen> grid) {
043         Jasen apujasen = new Jasen();
044         int eka = apujasen.ekaKentta();
045         int lkm = apujasen.getKenttia();
046         String[] headings = new String[lkm-eka];
047         for (int k=eka; k<lkm; k++) headings[k-eka] = apujasen.getKysymys(k);
048         grid.initTable(headings);
049 
050         for (int k=eka; k<lkm; k++) grid.setAlignment(k-eka, apujasen.getSijainti(k));
051         
052         grid.setOnCellString( (g, jasen, defValue, r, c) -> jasen.anna(c+eka) );
053         grid.setOnCellValue( (g, jasen, defValue, r, c) -> jasen.getAvain(c+eka) );
054 
055         grid.setTableMenuButtonVisible(true); // menu, josta voi valita sarakkeet
056         
057         grid.setEditable(true);
058         grid.setOnGridLiveEdit((g, jasen, defValue, r, c, edit) -> {
059             String virhe = jasen.aseta(c+eka,defValue);
060             if ( virhe == null ) {
061                 kerho.korvaaTaiLisaa(jasen); // jotta saadaan muutos
062                 edit.setStyle(null);
063                 Dialogs.setToolTipText(edit,"");
064             } else {
065                 edit.setStyle("-fx-background-color: red");
066                 Dialogs.setToolTipText(edit,virhe);
067             }
068             return defValue;
069         });
070         
071         grid.add(jasenet);
072     }
#

These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.