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 Sarakkeiden 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"); 
# otsikko

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); 
# asetuksia

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);
# rivinlisaaminen

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;
# oliohjarivi

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 erikseen. 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:

# vainolio

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;
        });

Huomioi tuo return lause - heittää hämärähkön virheilmoituksen ilman sitä

28 Apr 21

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
kloonata gitlabista:

https://gitlab.jyu.fi/tie/ohj2/esimerkit/fxexamples/-/tree/master/Examples/src

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

# V1

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);
    }
    ...
}    
# V2
# jasen

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

JasenStringGrid.java

File not found https://gitlab.jyu.fi/tie/ohj2/esimerkit/k2023/-/raw/main/luennot/live24/src/stringgrid/JasenStringGrid.java
# V3

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