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ých v 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ý na platformě. AWT a SWT využívá nativních služeb daného operačního systému (vzhled komponent) 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 z AWT, má komponenty napsané přímo v Javě, takže na všech platformách Swing aplikace vypadají stejně. Lze to však změnit pomocí tzv. LookAndFeel, tzn. metodami tří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říklad Java 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říte instance 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 Layoutu na 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 dispozici grafický designer. Ten není přímo v IDE, ale jedná se o samostatnou aplikaci, kterou pod názvem JavaFX 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 potomkem javafx.application.application. 5
Figure 5: Figure 6: 6
Figure 7: její základní metody, se kterými pracujeme, jsou: - launch(args): Slouží se spouš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 startu aplikace. V této metodě probíhá inicializace aplikace: @Override public 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.application a 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říkladu použ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. Obdoba http 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 kam patří) 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.css 2. 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. @Override public 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 v FXML 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 jejich Java 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"/> <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"/> <Label text="password:" GridPane.columnIndex="0" GridPane.rowIndex="2"/> <PasswordField GridPane.columnIndex="1" GridPane.rowIndex="2"/> <HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4"> <Button text="sign In"/> 14
</HBox> <Text GridPane.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ých objektů. <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ž jsou vyplněny obě pole. Pokud nechceme touto funkcionalitou zatěžovat Controller, můžeme použít jednoduchý 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 GUI a/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; @Override public void initialize(url location, ResourceBundle resources) { usernamefield.prefwidthproperty().bind(root.widthproperty()); Implementací rozhraní Initializable můžeme reagovat na vytvoření Controlleru. 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>() { @Override public 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 @Override public 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 Podobné s Radio Button. Figure 15: 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 i ná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 constructor TitledPane tp = new TitledPane("My Titled Pane", new Button("Button")); //applying methods TitledPane 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: @Override public 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 stack StackPane.setMargin(helpText, new Insets(0, 10, 0, 0)); // Center "?" hb.getchildren().add(stack); HBox.setHgrow(stack, Priority.ALWAYS); // Add stack pane to HBox object // 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-5 AnchorPane.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