Programovací jazyk Java
Přednáška 4
Základy práce s GUI
Grafická prostředí v Javě
Grafická prostředí jsou v Javě čtyři základní:
• nejstarší AWT (Abstract Windowing Toolkit),• novější JFC Swing (Java Foundation Classes),• SWT (Standard Widget Toolkit) a nejnovější JavaFX.
AWT
Součástí Java Core API od verze 1.0,
Malé množství komponent,
Omezená přenositelnost – závislost na odpovídajících objektech poskytovanýchv OS.
Vzhled definovaný podle operačního systému
AWT ve Windows
Figure 1:
1
Figure 2:
AWT v Linuxu
SWT
SWT je produktem IBM, je třeba doinstalovat příslušné knihovny, je závislý naplatformě.
AWT a SWT využívá nativních služeb daného operačního systému (vzhledkomponent) a na každém OS vypadá jinak, ale zase tak, jak jsou jeho uživatelézvyklí.
Nejznámější použitá je v IDE Elipse
Swing
součástí Java Core API od verze 1.2,
stejný princip použití jako AWT, mnoho tříd Swingu jsou podtřídami tříd zAWT,
má komponenty napsané přímo v Javě, takže na všech platformách Swing aplikacevypadají stejně. Lze to však změnit pomocí tzv. LookAndFeel, tzn. metodamitřídy javax.swing.UIManager (např. UIManager.getSystemLookAndFeelClassName()).
bohatý výběr komponent – stromy, seznamy, dialogy,
javax.swing – základní komponenty Swing GUI,
javax.swing.event – událostní komponenty Swing GUI.
V současnosti se využívá v podstatě již jen Swing a SWT.
Porovnání grafických prostředí Javy (česky) např. zde.
2
Figure 3:
3
Figure 4:
4
Základní vzhled Swing aplikace
JavaFX
Nejnovější, nejdřív součástí samostatného projektu (distribuce, jako napříkladJava SE, Java EE, . . . ).
Od verze Javy 8 oficiální náhrada technologie Swing.
JavaFX je moderní framework pro tvorbu bohatých okenních aplikací.
• JavaFX přináší podporu obrázků, videa, hudby, grafů, CSS stylů• Snadné vytvoření efektních aplikací• Zároveň je kladen důraz na jednoduchost tvorby, všechny zmiňované věci
jsou v JavaFX v základu.• JavaFX se hodí jak pro desktopové aplikace, tak pro webové applety nebo
mobilní aplikace.• Je možné ji také použit pro tvorbu 2D i 3D grafiky a různých animací
V JavaFX je možné vyvíjet podobně jako ve starším Swingu, tedy tak, že tvoříteinstance jednotlivých formulářových prvků (tlačítko, textové pole). Ty potévkládáte do tzv. layoutů, což jsou vlastně kontejnery na formulářové komponenty.Je možné ale FXML jazyk na definic vzhledu, podobně jako například Layoutuna platformě android.
Online tutoriál: Okenní aplikace v Java FX
Základní vzhled JavaFX aplikace
Vzhled JavaFX aplikace s použitím stylů
Java Scene Builder
Dobrou zprávou je, že FXML nemusíme psát ručně, existuje totiž k dispozicigrafický designer.
Ten není přímo v IDE, ale jedná se o samostatnou aplikaci, kterou pod názvemJavaFX Scene Builder stáhnete zde: JavaSceneBuilder.
Jednoduchá JavaFX aplikace Hello World
Vytvoření jednoduché aplikace jen pomocí kódu, bez FXML = základní principy.
Aplikace JavaFX je reprezentována jednou základní třídou, která je potomkemjavafx.application.Application.
5
Figure 5:
Figure 6:
6
Figure 7:
její základní metody, se kterými pracujeme, jsou: - launch(args): Slouží sespouštění JavaFX části aplikace
public class HelloWorld extends Application {
public static void main(String[] args) {launch(args);
}}
• public void start(Stage primaryStage): která se provolání při startuaplikace. V této metodě probíhá inicializace aplikace:
@Overridepublic void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");Button btn = new Button();btn.setText("Say 'Hello World'");btn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {System.out.println("Hello World!");
}});
StackPane root = new StackPane();root.getChildren().add(btn);primaryStage.setScene(new Scene(root, 300, 250));primaryStage.show();
7
}
Vysvětlení základních pojmů
• Třída Stage: objekt v jednoduchosti představující jedno okno aplikace.
• Třída Button: objekt představující tlačítko, zobrazující nějaký text,• metoda setOnAction() nastavuje událost, která se má stát v případě
stisku tlačítka,• třída EventHandler s metodou handle(ActionEvent event) představují
onu událost,• konstrukce <ActionEvent> je generický typ, viz další přednášky,• parametr metodu setOnAction() je takzvaná anonymní třída.
• Třída StackPane: (Pane) je panel na kterém se umísťují grafické objekty.
Figure 8:
JavaFX aplikace Login form
Vytvoříme jednoduchý formulář pro přihlášení:
Základ je opět třída Login.java, která je potomkem javafx.application.Applicationa obsahuje metody main(...) a start(...).
8
Figure 9:
Nastavení rozložení prvků
Layouty se starají o rozložení grafických prvků na panelu. V tomto příkladupoužijeme GridPane.
GridPane grid = new GridPane();grid.setAlignment(Pos.CENTER);grid.setHgap(10);grid.setVgap(10);grid.setPadding(new Insets(25, 25, 25, 25));
Scene scene = new Scene(grid, 300, 275);primaryStage.setScene(scene);
Tento GridPane pracuje jako tabulka, do jejich buněk umisťujeme prvky. Obdobahttp tabulce grid.
Přidání popisů a textových prvků
Prvky přidáváme do tabulky gridu a musíme určit jejich souřadnici (buňku kampatří)
9
Text scenetitle = new Text("Welcome");scenetitle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20));grid.add(scenetitle, 0, 0, 2, 1);
Label userName = new Label("User Name:");grid.add(userName, 0, 1);
TextField userTextField = new TextField();grid.add(userTextField, 1, 1);
Label pw = new Label("Password:");grid.add(pw, 0, 2);
PasswordField pwBox = new PasswordField();grid.add(pwBox, 1, 2);
Výsledné rozmístění prvků je následující:
Figure 10:
10
Tlačítko, textový prvek a zpráva události
Tlačítko
Button btn = new Button("Sign in");HBox hbBtn = new HBox(10);hbBtn.setAlignment(Pos.BOTTOM_RIGHT);hbBtn.getChildren().add(btn);grid.add(hbBtn, 1, 4);
Textového prvku pro zprávu uživateli
final Text actiontarget = new Text();grid.add(actiontarget, 1, 6);
Zpracování události
btn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {actiontarget.setFill(Color.FIREBRICK);actiontarget.setText("Sign in button pressed");
}});
Seznámení s CSS styly
Naším cílem bude vytvořit “hezčí formulář”
Figure 11:
11
Připojení Style sheets ke scéně
V kódu m metodě public void start(Stage primaryStage)
Scene scene = new Scene(grid, 300, 275);primaryStage.setScene(scene);scene.getStylesheets().add("Login.css");primaryStage.show();
a dále vytvoříme do adresáře src/main/resources:
1. soubor Login.css2. obrazovek pozadí background.jpg.
Nastylováni prvků v Login.css
Pozadí (root panel)
.root {-fx-background-image: url("background.jpg");
}
Popisky (Label)
.label {-fx-font-size: 12px;-fx-font-weight: bold;-fx-text-fill: #333333;-fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
}
Nastylováni konkrétní prvků
Nejprve odstraníme staré nastavení stylů přímo v kódu, řádky:
• scenetitle.setFont(Font.font(“Tahoma”, FontWeight.NORMAL,20));
• actiontarget.setFill(Color.FIREBRICK);
a nahradíme je definic ID prvku:
• scenetitle.setId("welcome-text");• actiontarget.setId("actiontarget");
na závěr vytvoříme pro prvky styl:
#welcome-text {-fx-font-size: 32px;-fx-font-family: "Arial Black";
12
-fx-fill: #818181;-fx-effect: innershadow( three-pass-box , rgba(0,0,0,0.7) , 6, 0.0 , 0 , 2 );
}#actiontarget {
-fx-fill: FIREBRICK;-fx-font-weight: bold;-fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
}
Posledním krokem je styl pro tlačítko
.button {-fx-text-fill: white;-fx-font-family: "Arial Narrow";-fx-font-weight: bold;-fx-background-color: linear-gradient(#61a2b1, #2A5058);-fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
}
.button:hover {-fx-background-color: linear-gradient(#2A5058, #61a2b1);
}
Použití FXML
FXML je odvozený z XML.
Slouží pro návrh prezentační části aplikace. Java se zde inspiruje a přenášíprincipy HTML a CSS do desktopových aplikací.
Podobný přístup je obecně rozšířen například v C#, Android, . . .
Propojení Scene s konkrétním FXML uděláme nejsnadněji v metodě Scene.
@Overridepublic void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(ClassLoader.getResource("fxml/Login.fxml"));
Scene scene = new Scene(root, 300, 275);
stage.setTitle("FXML Welcome");stage.setScene(scene);stage.show();
}
13
Použití prvků v FXML souboru
Pokud chceme použít nějaký prvek (TextField, Label, Button), musíme vFXML deklarovat import podobně jako ve třídě.
<?xml version="1.0" encoding="UTF-8"?><?import java.net.*?><?import javafx.geometry.*?><?import javafx.scene.control.*?><?import javafx.scene.layout.*?><?import javafx.scene.text.*?>
Můžeme použít import s *, což znamená použít všechny pvky v balíčku, nebo
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.ColumnConstraints?><?import javafx.scene.layout.GridPane?><?import javafx.scene.layout.RowConstraints?>
Prvky FXML jsou umístěny ve stejných balíčcích a mají stejné názvy jako jejichJava protějšky.
Definice FXML pro Login formulář
<GridPane alignment="center" hgap="10" vgap="10"><padding>
<Insets top="25" right="25" bottom="10" left="25"/></padding>
<Text text="Welcome"GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
<Label text="User Name:"GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<TextFieldGridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Label text="Password:"GridPane.columnIndex="0" GridPane.rowIndex="2"/>
<PasswordFieldGridPane.columnIndex="1" GridPane.rowIndex="2"/>
<HBox spacing="10" alignment="bottom_right"GridPane.columnIndex="1" GridPane.rowIndex="4"><Button text="Sign In"/>
14
</HBox>
<TextGridPane.columnIndex="1" GridPane.rowIndex="6"/>
</GridPane>
Nastylováni FXML formuláře
K propojení FXML dokumentu se stylem musíme:
1. Říci že chceme styl použít, a jaký:
<GridPane stylesheets="Login.css" ...
2. Dále je možné přiřadit elementům css class:
<GridPane styleClass="root" ...
3. Dále je možné přiřadit elementům css id:
<Text id="welcome-text" ...
4. Eventuálně je možné použít styl přímo
<Label style="-fx-font: NORMAL 20 Tahoma; ...
Propojení formuláře s Java třídou
K FXML dokumentu je možné připojit takzvaný Controller.
V FXML je pro prvky sloužící k propojení s třídami použit Namespace fx.
Namespace a controller se definují v kořenovém elementu FXML dokumentu.
<GridPane xmlns:fx="http://javafx.com/fxml"fx:controller="cz.mendelu.pjj.javafx.LoginController"alignment="center" hgap="10" vgap="10">
Controller je obyčejná jJava třída, využíváme v injection pro získání grafickýchobjektů.
<Text fx:id="actiontarget"GridPane.columnIndex="1" GridPane.rowIndex="6"/>
15
public class LoginController {
@FXML private Text actiontarget;}
Reakce na události ve formuláři
Pokud chceme reagovat na událost, použijeme ve FXML příslušný atribut:on*="#metoda"
<Button text="Sign In" onAction="#handleSubmitButtonAction"/>
V Controlleru pak vytvoříme příslušnou metodu a se správnými parametry.
public class LoginController {
@FXML private Text actiontarget;
@FXML protected void handleSubmitButtonAction(ActionEvent event) {actiontarget.setText("Sign in button pressed");
}}
Skriptování
Někdy potřebujeme “jen” aby dva různé prvky formuláře na sebe reagovaly.
Například pokud chceme povolit kliknout na tlačítko jen když jsouvyplněny obě pole.
Pokud nechceme touto funkcionalitou zatěžovat Controller, můžeme použítjednoduchý script přímo v FXML souboru.
FXML lze scriptovat v JavaScript, Groovy, Jython a Clojure.
1. Musíme script povolit
<?language javascript?>
2. Přiřadím id TextFild a Button prvkům a zakáži používat tlačítko
<TextField fx:id="userNameField" ...
<PasswordField fx:id="passwordField" ...
<Button fx:id="signInButton" text="Sign In" disable="true" ...
16
3. Vytvořím funkci
<fx:script>function editAction() {
signInButton.setDisable(userNameField.text.isEmpty()|| passwordField.text.isEmpty());
}</fx:script>
4. Funkci zaregistruji k události
<TextField fx:id="userNameField"* onKeyReleased="editAction(event);"
GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<PasswordField fx:id="passwordField"* onKeyReleased="editAction(event);"
GridPane.columnIndex="1" GridPane.rowIndex="2"/>
Binding dat (property)
JavaFX má mechanizmus který automaticky aktualizuje data mezi prvky GUIa/nebo modelem.
Příklad 1. Roztahování položek v okně
Toto je spíš demonstrační ruční varianta, používejte správné Layouts!
public class LoginController implements Initializable {
@FXML private GridPane root;@FXML private TextField userNameField;
@Overridepublic void initialize(URL location, ResourceBundle resources) {
userNameField.prefWidthProperty().bind(root.widthProperty());}
}
Implementací rozhraní Initializable můžeme reagovat na vytvoření Con-trolleru.
Příklad 2. Roztahování položek v okně
Nejprve vytvoříme model, což bude prezentovat třída User
17
public class User {
private StringProperty userName = new SimpleStringProperty();
public User() {userName.addListener(new ChangeListener<String>() {
@Overridepublic void changed(ObservableValue<? extends String> observable,
String oldValue,String newValue) {
System.out.format("New userName: %s\n", newValue);}
});}
public String getUserName() { return userName.get(); }
public StringProperty userNameProperty() {return userName;
}
public void setUserName(String userName) { this.userName.set(userName); }}
Vytvořím instanci ve třídě Login
public static final User USER = new User();
A propojím s GUI prvkem pomocí Controlleru
@Overridepublic void initialize(URL location, ResourceBundle resources) {
userNameField.prefWidthProperty().bind(root.widthProperty());* userNameField.textProperty()* .bindBidirectional(Login.USER.userNameProperty());}
Do main metody přidáme vlákno umožňují editaci jména.
public static void main(String[] args) {new Thread(() -> {
try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in))) {
String name = null;while ((name = in.readLine()) != null) {
USER.userNameProperty().setValue(name);}
} catch (IOException e) {
18
e.printStackTrace();}
}).start();launch(args);
}
Přehled základních prvků GUI
Kompletní přehled základních prvků zde
Existují knihovny s dalšími prvky například: ControlsFX, JFXtras.
Label
Figure 12:
• Slouží k zobrazování textu.• Můžou na něj být aplikovány různé vizuální efekty.• Minimální uživatelská interakce.
Button
• Prvky reagující na klik uživatele.• Stylují se podle pozice kurzoru.
Radio Button
• Slouží na výběr mezi několika možnostmi.
19
Figure 13:
Figure 14:
20
• Seskupují se do skupin: ToggleGroup.
final ToggleGroup group = new ToggleGroup();
RadioButton rb1 = new RadioButton("Home");rb1.setToggleGroup(group);rb1.setSelected(true);
RadioButton rb2 = new RadioButton("Calendar");rb2.setToggleGroup(group);
RadioButton rb3 = new RadioButton("Contacts");rb3.setToggleGroup(group);
Toggle Button
Figure 15:
• Podobné s Radio Button.
Checkbox
• Prvek poskytující boolean hodnotu.
CheckBox cb1 = new CheckBox();cb1.setText("First");cb1.setSelected(true);
Choice Box
• Umožňuje uživateli zvolit jednu z nabízených možnost.
21
Figure 16:
Figure 17:
• Většinou se jedná o textové volby definované polem nebo kolekcí.
ChoiceBox cb = new ChoiceBox(FXCollections.observableArrayList("First","Second","Third"));
Text Field
• Nejedná se jen o hloupý prvek pro zadávání textu, ale dovede zobrazovat inápovědu a chyby.
22
Figure 18:
Scroll Bar a Scroll Pane
• Scroll Bar je posuvnuk pro nastavení číselné hodnoty.• Scroll Pane je prvek umožňující posouvat jiné prvky.
List View
• Zobrazení seznamu hodnot, podobně jako Choice Box.• Dle nastavení je možné vypírat jeden nebo i více prvků.
ListView<String> list = new ListView<>();ObservableList<String> items =FXCollections.observableArrayList (
"Single",
23
Figure 19:
"Double","Suite","Family App");
list.setItems(items);
Table View
Tree View
Combo Box
• Umožňuje uživateli zvolit jednu položky z textové nabídky.
ObservableList<String> options = FXCollections.observableArrayList("Option 1","Option 2","Option 3"
);final ComboBox comboBox = new ComboBox(options);
Separator
• Odděluje prvky GUI graficky od sebe.• Má jen vizuální význam.
Slider
Progress Bar and Progress Indicator
• Indikace stavu postupu v nějakém procesu.• Eventuálně se používá pouze jako indikátor, že se provádí výpočet.
24
Figure 20:
25
Figure 21:
Figure 22:
26
Figure 23:
Figure 24:
Figure 25:
27
ProgressBar pb = new ProgressBar(0.6);ProgressIndicator pi = new ProgressIndicator(0.6);
Tooltip
Figure 26:
• Nápověda nebo validace u prvků.
final PasswordField pf = new PasswordField();final Tooltip tooltip = new Tooltip();tooltip.setText(
"\nYour password must be\n" +"at least 8 characters in length\n" +
);pf.setTooltip(tooltip);Image image = new Image(
getClass().getResourceAsStream("warn.png"));tooltip.setGraphic(new ImageView(image));
Titled Pane and Accordion
Figure 27:
28
• Rozbalující prvek zobrazující (obsahující) další prvky.
//using a two-parameter constructorTitledPane tp = new TitledPane("My Titled Pane", new Button("Button"));//applying methodsTitledPane tp = new TitledPane();tp.setText("My Titled Pane");tp.setContent(new Button("Button"));
Menu
Figure 28:
• JavaFX umožňuje vytvářet různě koplikovaná menu
MenuItem clear = new MenuItem("Clear");clear.setAccelerator(KeyCombination.keyCombination("Ctrl+X"));clear.setOnAction((ActionEvent t) -> {
vbox.setVisible(false);});
Date Picker
• Prvek pro zadávání data a času
Pagination Control
• JavaFX umožňuje i stránkování obsahu.
Canvas
• Canvas je prvek sloužící ke kreslení vlastní grafiky.
29
Figure 29:
Figure 30:
30
Figure 31:
@Overridepublic void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Operations Test");Group root = new Group();Canvas canvas = new Canvas(300, 250);GraphicsContext gc = canvas.getGraphicsContext2D();gc.setFill(Color.GREEN);gc.setStroke(Color.BLUE);gc.setLineWidth(5);gc.strokeLine(40, 10, 10, 40);gc.fillOval(10, 60, 30, 30);gc.strokeOval(60, 60, 30, 30);root.getChildren().add(canvas);primaryStage.setScene(new Scene(root));primaryStage.show();
}
Rozložení prvků: Layouts
Grafické prvky na scéně vytvářejí ve stromové hierarchii listy.
Uzly (tedy jejich nadřazené prvky) tvoří kontejnery s layouty, které rozhodují,kde se jaký prvek zobrazí a jakou bude mít velikost.
BorderPane
BorderPane border = new BorderPane();HBox hbox = addHBox()border.setTop(hbox);border.setLeft(addVBox());
31
Figure 32:
addStackPane(hbox); // Add stack to HBox in top region
border.setCenter(addGridPane());border.setRight(addFlowPane());
VBox / HBox
HBox hbox = new HBox();hbox.setPadding(new Insets(15, 12, 15, 12));hbox.setSpacing(10);hbox.setStyle("-fx-background-color: #336699;");
Button buttonCurrent = new Button("Current");buttonCurrent.setPrefSize(100, 20);
Button buttonProjected = new Button("Projected");
32
buttonProjected.setPrefSize(100, 20);hbox.getChildren().addAll(buttonCurrent, buttonProjected);
StackPane
Figure 33:
Velmi užitečný panel, zobrazí vždy právě jednu svoji komponentu.
StackPane stack = new StackPane();Rectangle helpIcon = new Rectangle(30.0, 25.0);helpIcon.setFill(new LinearGradient(0,0,0,1, true, CycleMethod.NO_CYCLE,
new Stop[]{new Stop(0,Color.web("#4977A3")),new Stop(0.5, Color.web("#B0C6DA")),new Stop(1,Color.web("#9CB6CF")),}));
helpIcon.setStroke(Color.web("#D0E6FA"));helpIcon.setArcHeight(3.5);helpIcon.setArcWidth(3.5);
Text helpText = new Text("?");helpText.setFont(Font.font("Verdana", FontWeight.BOLD, 18));helpText.setFill(Color.WHITE);helpText.setStroke(Color.web("#7080A0"));
stack.getChildren().addAll(helpIcon, helpText);stack.setAlignment(Pos.CENTER_RIGHT); // Right-justify nodes in stackStackPane.setMargin(helpText, new Insets(0, 10, 0, 0)); // Center "?"
hb.getChildren().add(stack); // Add stack pane to HBox objectHBox.setHgrow(stack, Priority.ALWAYS); // Give stack any extra space
GridPane
FlowPane
AnchorPane
AnchorPane anchorpane = new AnchorPane();Button buttonSave = new Button("Save");
33
Figure 34:
Figure 35:
34
Figure 36:
Button buttonCancel = new Button("Cancel");
HBox hb = new HBox();hb.setPadding(new Insets(0, 10, 10, 10));hb.setSpacing(10);hb.getChildren().addAll(buttonSave, buttonCancel);
anchorpane.getChildren().addAll(grid,hb); // Add grid from Example 1-5AnchorPane.setBottomAnchor(hb, 8.0);AnchorPane.setRightAnchor(hb, 5.0);AnchorPane.setTopAnchor(grid, 10.0);
Výsledné složení dohromady
3D grafika
JavaFX umožňuje používat také 3D grafiku pro tvorbu GUI.
Demo aplikace youtube
Zdoj pro samostudiu
http://docs.oracle.com/javase/8/javase-clienttechnologies.htm
http://www.java2s.com/Tutorials/Java/JavaFX/index.htm
http://www.itnetwork.cz/java/javafx
35
Figure 37:
Děkuji za pozornost!
36