Minulla on IntelliJ / Kotlin -sovellus, johon olen tehnyt käyttöliittymän siten, että piirrän kaikki buttonit, labelit, kuvat ja vastaavat itse formin canvakselle.
Tämä toimii oikein hyvin, mutta nyt haluaisin tehdä saman sovelluksen oikeilla käyttöliittymäkomponenteilla JavaFX:ää ja Scene Builderiä käyttäen. Osa käyttöliittymän komponenteista pitäisi alustaa koodissa käynnistyksen yhteydessä, mutta en löydä paikkaa, mihin alustuksen voisi kirjoittaa.
Laitan tähän näkyviin IntelliJ:n JavaFX HelloApplication-projektin koodin.
Kuinka tähän lisätään heti sovelluksen käynnityksessä kutsuttava funktio, joka asettaa welcomeText-labeliin vaikkapa "Ohjelmointiputka". Mihin tämä funktio kirjoitetaan ja mistä sitä kutsutaan?
HelloApplication.kt:
package com.example.demo import javafx.application.Application import javafx.fxml.FXMLLoader import javafx.scene.Scene import javafx.stage.Stage class HelloApplication : Application() { override fun start(stage: Stage) { val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource("hello-view.fxml")) val scene = Scene(fxmlLoader.load(), 320.0, 240.0) stage.title = "Hello!" stage.scene = scene stage.show() } } fun main() { Application.launch(HelloApplication::class.java) }
HelloController.kt:
package com.example.demo import javafx.fxml.FXML import javafx.scene.control.Label class HelloController { @FXML private lateinit var welcomeText: Label @FXML private fun onHelloButtonClick() { welcomeText.text = "Welcome to JavaFX Application!" } }
hello-view.fxml:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.Button?> <VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.demo.HelloController"> <padding> <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/> </padding> <Label fx:id="welcomeText"/> <Button text="Hello!" onAction="#onHelloButtonClick"/> </VBox>
Mikset luo initComponents() -metodia, ja kutsu sitä konstruktorissa ennen ohjelman muuta toimintaa.
Mitä olen Swing-komponenttien kanssa ohjelmoinut, niin AMK:n Ohjelmoinnin jatkokurssilla, kun niitä käsiteltiin, niin sielläkin pistettiin alustukset tuon GUI-komponentin esittelyn yhteyteen, jonka näkyvyysalueeksi määriteltiin private, mahdollisteti myos muuttuja oli staattinen static avainsanalla.
Mulla kävi yksi aika ennen kesää 2021 mielessä noihin Swing-komponentteihin liittyvän Boilerplaten, en tiedä tarkkaa suomennosta, ohjelmointia, niin siinä ideoin tuota, että pitäisi olla jokin alustusmetodi, jota sitten konstruktori kutsuu,että ei tarvitsisi GUI-komponentin esittelyn yhteydessä luoda sitä olio-ilmentymää. Koodista tulee järkyttävän näkoista nimittäin, jos noita graafisen käyttoliittymän komponentteja on useampia.
Kävi mielessä tuossa sen jälkeen, kun olin vastaillut ja luin muita säikeitä täällä Ohjelmointiputkassa, ja sitten kävin tupakalla, niin tuli mieleen vielä parempi ratkaisu, miten varmaan itse toteuttaisin tuo, eli
1. Luo rajapinta, jossa on pelkät GUI-komponenttien esittelyt
2. Luo uusi tietotyyppi Esim. CompInit, jossa on toteutettuna tuo aiemmin luotu rajapinta, ja setterit ja getterit niiden komponenttioliomuuttujien luonnin lisäksi.
3. Sittenhän selviät vain yhden muuttujan käytollä pääohjelmassasi, kun esittelet ja luot tuosta kakkoskohdassa luodusta tietotyypistä oliomuuttujan, josta sitten pistenotaatiolla, kun viittaat jonkin komponentin get-metodiin, niin sittenhän pääset käsiksi niiden komponenttien alkuperäisiin valmiiksi määriteltyihin metodeihin.
Tuo taitaa olla järkevä ratkaisu.
Jere Sumell kirjoitti:
Mikset luo initComponents() -metodia, ja kutsu sitä konstruktorissa ennen ohjelman muuta toimintaa.
Juuri näin olen yrittänyt tehdä, mutta en ole löytänyt konstruktoria, se taitaa olla jossain Scene Builderin sisuksissa.
Kysymykseni oli siis, mihin tuo initComponents() -metodi lisätään ja mihin sen kutsu tulee? Vastauksen haluaisin ylläolevaan koodiin päivitettynä!
Tässä koodia Swing-komponetein tuo edellinen rajapinta/tietyyppi-luonti ohjeistus
Rajapinta
import javax.swing.*; public interface MyComps { //Attribuutit static JButton myButton=null; //Metodit public void setMyButton(); public JButton getMyButton(); }
tietotyyppi, jossa toteutetaan edellinen rajapinta
import javax.swing.JButton; public class CompInit implements MyComps { private JButton myButton; @Override public void setMyButton() { this.myButton = new JButton("My Button"); } @Override public JButton getMyButton() { return this.myButton; } }
Sitten pääohjelma
import javax.swing.*; import java.awt.*; public class ButtonDemo extends JFrame { private CompInit init; public ButtonDemo() { setSize(352,288); setTitle("GUI-demo"); setDefaultCloseOperation(EXIT_ON_CLOSE); init = new CompInit(); init.setMyButton(); startti(); } public void startti() { Container con = getContentPane(); init.getMyButton().setText("My Button text modified"); con.add(init.getMyButton()); } public static void main(String[] args) { ButtonDemo demo = new ButtonDemo(); demo.setVisible(true); } }
Kiitos vain, mutta kysymykseni koskee JavaFX:ää ja sen Scene Builderia.
En ole käyttänyt JavaFX:ää enkä tietysti testannut näitä ratkaisuja, mutta googletuksen perusteella ehdottaisin:
Kun kontrolleri on luotu, kutsutaan sen initialize-metodia. Eli jos teet sinne kyseisen metodin, voit siinä muuttaa tekstiä.
@FXML public fun initialize() { welcomeText.text = "Ohjelmointiputka" }
Toinen (varmasti huonompi) vaihtoehto olisi säätää jo tuolla start-metodissa:
val root = fxmlLoader.load() val scene = Scene(root, 320.0, 240.0) val hc = root.getController() if (hc is HelloController) { hc.kutsutaanPublicFun() }
Kiitos!
Toimii.
Nyt kun tiesin, mitä funktiota etsiä, niin löysin sen sitten googlesta itsekin.
Aihe on jo aika vanha, joten et voi enää vastata siihen.