You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 12 Next »

Introduksjon

I denne leksjonen vil du lære hvordan du kan koble FXML-koden til Java-koden og gjøre app-en levende (interaktiv)!

Steg 1: Opprette app-pakke, app-klasse, FXML-fil og controller-klasse.

Lag først en ny app-pakke og app-klasse som forklart i Hello world-leksjonen. Bruk kalkulator som navn på pakken og KalkulatorApp som navn på app-klassen.

Åpne KalkulatorApp-klassen og rediger slik at du får følgende kode:

package kalkulator;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;


public class KalkulatorApp extends Application {
	public void start(Stage primaryStage) throws IOException {
		FXMLLoader fxmlLoader = new FXMLLoader(KalkulatorApp.class.getResource("KalkulatorApp.fxml"));
		Pane root = fxmlLoader.load();
		primaryStage.setScene(new Scene(root));
		primaryStage.show();
	}

	public static void main(String[] args) {
		launch(KalkulatorApp.class, args);
	}
}

Dette er nesten akkurat samme kode som i FxmlLogoApp fra FXML-logo-leksjonen, vi har bare byttet ut FxmlLogoApp med KalkulatorApp på fire steder: etter public class, KalkulatorApp.class og KalkulatorApp.fxml i start-metoden og KalkulatorApp.class i main-metoden). Når du senere skal lage app-klasser, så kan du gjøre det samme: Ta denne koden og putt inn navnet på den nye klassen på de riktige stedene.

Lag en ny FXML-fil ved navn KalkulatorApp.fxml som forklart i FXML-logo-leksjonen.

Rediger KalkulatorApp.fxml-fila med FXML-editoren og/eller SceneBuilder, slik at du får en kalkulator med knapper (type Button) for hvert tall 0-9, desimalpunktum, de fire regneartene +, -, *, / og likhetstegn =. Over disse har du et tekstfelt (TextField).

Det kan se ut omtrent som vist til høyre.

Hvis du kjører KalkulatorApp med Run As > Java Application, så vil du få opp et vindu som ser ut som en kalkulator, men som ikke gjør noe. Dvs. du kan trykke på knappene og skrive i tekstfeltet, men app-en reagerer jo ikke som en vanlig kalkulator. Vi ønsker f.eks. at hvis du trykker på knappene 1, 2, +, 3, 4, = så skal resultatet 46.0 vises i tekstfeltet. For å få det til må vi lage en egen KalkulatorController-klasse, som skal ha kode for å reagere på knappetrykkene, gjøre utregningene og hele tiden oppdatere tekstfeltet, som en ekte kalkulator ville gjort. Lag KalkulatorController-klassen ved å høyreklikke på kalkulator-pakken og velge New > Class og fylle inn KalkulatorController som navn.

Det neste trinnet er å fortelle JavaFX at denne klassen skal være controller-klassen til app-en. Dette gjøres ved å legge inn fx:controller="kalkulator.KalkulatorController" i FXML-koden for kalkulator-panelet, altså KalkulatorApp.fxml. Hvis du f.eks. bruker en AnchorPane som panel så vil koden se omtrent slik ut:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.text.*?>
... flere <?import ... ?> her ...
<?import javafx.scene.control.TextField?>

<AnchorPane prefHeight="126.0" prefWidth="222.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
	fx:controller="kalkulator.KalkulatorController">

 
	... alle knappene kommer her ...
 
</AnchorPane>

Det vesentlig her er fx:controller="kalkulator.KalkulatorController", som forteller at KalkulatorController er klassen som skal reagere på og styre knappene og tekstfeltet i KalkulatorApp.fxml.

Steg 2: Koble FXML-fil og Controller sammen

Hvis du har lagt inn fx:controller="kalkulator.KalkulatorController"KalkulatorApp.fxml, så vil det automagisk bli laget en KalkulatorController når FXML-en lastes inn (av FXMLLoader-en i KalkulatorApp sin start-metode). Det som da gjenstår er å koble KalkulatorController-en til knappene og tekstfeltet, og tenke ut hva som er riktig kalkulatoren-logikk. Kalkulator-logikken er ikke så enkel som en kan tro, så vi tar sammenkoblingen først.

Sammenkobling går to veier:

  1. Fra FXML til Controller: knappene i KalkulatorApp.fxml sier fra til KalkulatorController når de trykkes på.
  2. Fra Controller til FXML: KalkulatorController oppdaterer tekstfeltet i KalkulatorApp.fxml når tilstanden inni kalkulatoren endres, f.eks. når den beregner en ny verdi som skal vises frem.

I begge tilfeller må en legge til kode i både Kalkulator.fxml og KalkulatorController, men på ulike måter.

Fra FXML til Controller

Alle elementene i FXML-fila kan si fra om hva brukeren gjør med dem. Knapper kan f.eks. si fra når de trykkes på, og tekstfelt kan si fra når teksten inn endres. Dette kalles hendelser (eng: events) og det vi må gjøre er å legge inn koder i FXML-fila som angir hvilken metode i Controller-en som skal kalles for hver type hendelse på de ulike elementene. Hendelsen "knappetrykk" kalles action, og derfor legger en inn onAction="#handleButton" i FXML-koden for en knapp for å kalle handleButton-metoden når akkurat den knappen trykkes. Hvis forskjellige knapper skal ha omtrent samme logikk, så kan en bruke samme metode. F.eks. så skal kalkulatoren gjøre omtrent det samme for alle tall-knappene, så derfor kan en legge onAction="#handleDigitButton" på alle disse. En må selvsagt huske på å lage handleDigitButton-metoden også. Her er eksempelkode for tall-knappene:

I KalkulatorController.java:

public class KalkulatorController {
 
	... annen kode ...
 
	@FXML
	void handleDigitButton(ActionEvent event) {
		// denne setningen skriver ut hvilken knapp som kalte metoden
		System.out.println(event.getSource());
		... her er kode for tall-knappene ...
	}
}

@FXML brukes for å gi FXML-en lov til å kalle metoden, mens void betyr at metoden ikke skal returnere noen verdi.

event-parameteret (av typen ActionEvent) inneholder informasjon om hendelsen. F.eks. kan en hente ut hvilken knapp som ble trykket med event.getSource(). Her skriver vi ut denne verdien, for å sjekke at det virker som det skal. Hvis det ikke er viktig å vite hvilken knapp som ble trykket (f.eks. hvis den bare kalles av én knapp), så trenger du ikke ha med event-parameteret i metoden.

I KalkulatorApp.fxml

<?xml version="1.0" encoding="UTF-8"?>

<AnchorPane ...	fx:controller="kalkulator.KalkulatorController">
      <Button text="1" onAction="#handleDigitButton" ... />
      <Button text="2" onAction="#handleDigitButton" ... />
 
	... de andre knappene kommer her ...
 
</AnchorPane>

Både 1- og 2-knappen har onAction="#handleDigitButton", så for begge disse vil handleDigitButton blir kalt når de trykkes på.

Prøv å gjøre endringene vist over, og så om du får skrevet ut hvilken knapp som trykkes. Husk at det bare er knappene som har onAction="#handleDigitButton" som vil trigge utskriften!

Fra Controller til FXML

Fra Kalkulator.fxml til KalkulatorController



Hva har du lært?

  • å koble FXML-koden til Java-koden med fx:id- og onAction-attributter

P.S.

Her er én av mange løsninger:

package kalkulator;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
public class KalkulatorController {
	private double value1 = 0.0;
	private String value2 = null;
	private String operator = null;
	@FXML
	TextField valueText;
	
	@FXML
	void initialize() {
		updateValueField();
	}
	private void updateValueField() {
		if (value2 != null) {
			valueText.setText(value2);
			validateValue2();
		} else {
			valueText.setText(String.valueOf(value1));
		}
	}
	@FXML
	void handleValueTextChanged(StringProperty prop, String oldValue, String newValue) {
		value2 = valueText.getText();
		validateValue2();
	}
	private void validateValue2() {
		try {
			Double.valueOf(value2);
			valueText.setStyle(null);
		} catch (NumberFormatException e) {
			valueText.setStyle("-fx-border-color: red;");
		}
	}
	private void appendToValue2(String s) {
		if (value2 == null) {
			value2 = "";
		}
		value2 = value2 + s;
		updateValueField();
	}
	private void performOperation() {
		if (value2 != null) {
			double val2 = Double.valueOf(value2);
			if (operator != null) {
				switch (operator) {
				case "/": value1 = value1 / val2; break;
				case "*": value1 = value1 * val2; break;
				case "+": value1 = value1 + val2; break;
				case "-": value1 = value1 - val2; break;
				}
				operator = null;
			} else {
				value1 = val2;
			}
			this.value2 = null;
		}
		updateValueField();
	}
	private void registerOperator(String op) {
		performOperation();
		operator = op;
		updateValueField();
	}
	
	@FXML
	void digit(ActionEvent event) {
		Button button = (Button) event.getSource();
		appendToValue2(button.getText());
	}
	
	@FXML void divide() {
		registerOperator("/");
	}
	@FXML void multiply() {
		registerOperator("*");
	}
	
	@FXML void add() {
		registerOperator("+");
	}
	@FXML void subtract() {
		registerOperator("-");
	}
	@FXML void desimalPoint() {
		appendToValue2(".");
	}
	@FXML void compute() {
		performOperation();
	}
}

 

 

  • No labels