Difference between revisions of "컴퓨터프로그래밍및실습 (2022년)/1128"
Jump to navigation
Jump to search
Line 707: | Line 707: | ||
</table2> | </table2> | ||
<gallery> | |||
file:fruit1.jpg | fruit1.jpg | |||
file:fruit2.jpg | fruit2.jpg | |||
file:fruit3.jpg | fruit3.jpg | |||
file:fruit4.jpg | fruit4.jpg | |||
file:fruit5.jpg | fruit5.jpg | |||
</gallery> | |||
[[file:CP-17.4.6-실행결과.png | thumb | 실행 결과]] | |||
<syntaxhighlight lang="xml"> | <syntaxhighlight lang="xml"> | ||
<?xml version="1.0" encoding="UTF-8"?> | |||
<?import javafx.scene.image.ImageView?> | |||
<?import javafx.scene.layout.TilePane?> | |||
<TilePane xmlns:fx="http://javafx.com/fxml" prefHeight="100.0" prefWidth="100.0"> | |||
<children> | |||
<ImageView> | |||
<image><Image url="@images/fruit1.jpg" /></image> | |||
</ImageView> | |||
<ImageView> | |||
<image><Image url="@images/fruit2.jpg" /></image> | |||
</ImageView> | |||
<ImageView> | |||
<image><Image url="@images/fruit3.jpg" /></image> | |||
</ImageView> | |||
<ImageView> | |||
<image><Image url="@images/fruit4.jpg" /></image> | |||
</ImageView> | |||
<ImageView> | |||
<image><Image url="@images/fruit5.jpg" /></image> | |||
</ImageView> | |||
</children> | |||
</TilePane> | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Revision as of 20:25, 21 July 2022
JavaFX 개요
- AWT
- Native UI 컴포넌트 사용
- 운영체제 마다 UI 모양이 다름
- Swing
- 운영체제가 제공하는 native UI 사용 안 함
- 운영체제가 새롭게 제공하는 UI 지원의 어려움
- JavaFX
- Abode의 flash, Microsoft의 silverlight의 대항마
- JDK 7부터 지원. JDK 8 권장.
- JDK 11부터는 별도로 설치해야 함.
- 화면 레이아웃과 스타일, 애플리케이션 로직 분리
- Java 코드와 분리해서 스타일 시트(CSS)로 외관 작성 → 개발자와 디자이너의 동시 개발 가능
- Java 코드에서도 레이아웃과 애플리케이션 로직을 분리하고 싶다면 레이아웃은 FXML로 작성, 로직은 Java로 작성
- JavaFX 애플리케이션 구성 요소
[레이아웃] 자바 코드 파일 또는 FXML 파일 |
[외관 및 스타일] CSS 파일 |
[리소스] 그림 파일 동영상 파일 ... |
[비즈니스 로직] 자바 코드 파일 |
JavaFX 애플리케이션 개발 시작
메인 클래스
import javafx.application.Application;
import javafx.stage.Stage;
public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
JavaFX 라이프사이클(life cycle)
- 예제 코드
import javafx.application.Application;
import javafx.stage.Stage;
public class AppMain extends Application {
public AppMain() {
System.out.println(Thread.currentThread().getName() + ": AppMain() 호출");
}
@Override
public void init() throws Exception {
System.out.println(Thread.currentThread().getName() + ": init() 호출");
}
@Override
public void start(Stage primaryStage) throws Exception {
System.out.println(Thread.currentThread().getName() + ": start() 호출");
primaryStage.show();
}
@Override
public void stop() throws Exception {
System.out.println(Thread.currentThread().getName() + ": stop() 호출");
}
public static void main(String[] args) throws Exception {
System.out.println(Thread.currentThread().getName() + ": main() 호출");
launch(args);
}
}
- 실행 결과
main: main() 호출
JavaFX Application Thread: AppMain() 호출
JavaFX-Launcher: init() 호출
JavaFX Application Thread: start() 호출
JavaFX Application Thread: stop() 호출 ← 프로그램을 끝내야 실행된다.
메일 클래스 실행 매개값 얻기
C:> java AppMain --ip=192.168.0.5 --port=50001
- main()에서 launch(args)를 넘겨 받음
- init() 메소드에서 아래와 같이 실행할 수 있음
Parameters params = getParameters();
List<String> list = params.getRaw();
Map<String, String> map = params.getNamed();
무대(Stage)와 장면(Scene)
- 윈도우 : Stage
- Stage에는 하나의 Scene을 가질 수 있음
- Scene은 직접 생성해야 함
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
root.setPrefWidth(350);
root.setPrefHeight(150);
root.setAlignment(Pos.CENTER);
root.setSpacing(20);
Label label = new Label();
label.setText("Hello, JavaFX");
label.setFont(new Font(50));
Button button = new Button();
button.setText("확인");
button.setOnAction(event->Platform.exit());
root.getChildren().add(label);
root.getChildren().add(button);
Scene scene = new Scene(root); // VBox를 루트 컨테이너(root container)로 해서 Scene 생성
primaryStage.setTitle("AppMain입니다");
primaryStage.setScene(scene); // 윈도우에 장면 설정
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
JavaFX 레이아웃
프로그램적 레이아웃
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
HBox hbox = new HBox();
hbox.setPadding(new Insets(10));
hbox.setSpacing(10);
TextField textField = new TextField();
textField.setPrefWidth(200);
Button button = new Button();
button.setText("확인");
ObservableList list = hbox.getChildren();
list.add(textField);
list.add(button);
Scene scene = new Scene(hbox);
primaryStage.setTitle("AppMain");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML 레이아웃
- FXML 파일 (root.fxml)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<HBox xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets top="10" right="10" bottom="10" left="10"/>
</padding>
<spacing>10</spacing>
<children>
<TextField>
<prefWidth>200</prefWidth>
</TextField>
<Button>
<text>확인</text>
</Button>
</children>
</HBox>
- AppMain 클래스
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("root.fxml")); // ← FXML 로드
Scene scene = new Scene(root);
primaryStage.setTitle("AppMain");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
레이아웃(layout) 여백: 패딩(padding)과 마진(margin)
- Margin, Padding 설정 방법
// top, right, bottom, left를 모두 동일한 값으로 설정할 때
// Insets(double topRightBottomLeft)
new Insets(50);
// top, right, bottom, left를 다를 값으로 설정할 때
// Insets(double top, double right, double bottom, double left)
new Insets(10, 20, 30, 40);
- 패딩과 마진 적용 예
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// 패딩 설정
HBox hbox = new HBox();
hbox.setPadding(new Insets(50, 10, 10, 50));
Button button = new Button();
button.setPrefSize(100, 100);
// 마진 설정
// HBox hbox = new HBox();
// Button button = new Button();
// button.setPrefSize(100, 100);
// HBox.setMargin(button, new Insets(10, 10, 50, 50));
hbox.getChildren().add(button);
Scene scene = new Scene(hbox);
primaryStage.setTitle("AppMain");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML 작성 규칙
프로그램적 레이아웃 자바 코드 | FXML 레이아웃 태그 |
---|---|
HBox hbox = new HBox(); hbox.setPadding(new Inset(10, 10, 10, 10)); hbox.setSpacing(10); |
<HBox xmlns:fx="http://javafx.com/fxml"> <padding> <Insets top="10" right="10" bottom="10" left="10"/> </padding> </HBox> |
TextField textField = new TextField(); textField.setPrefWidth(200); |
<TextField> <prefWidth>200</prefWidth> </TextField> |
Button button = new Button(); button.setText("확인"); |
<Button> <text>확인</text> </Button> |
ObsetvableList list = hbox.getChildren(); list.add(textField); list.add(button); |
<children> <TextField> <prefWidth>200</prefWidth> </TextField> <Button> <text>확인</text> </Button> </children> |
패키지(package) 선언
자바 코드 | FXML 태그 |
---|---|
import javafx.scene.layout.HBox; | <?import javafx.scene.layout.HBox?> |
import javafx.scene.control.*; | <?import javafx.scene.control.*?> |
- <?import?>가 들어가는 위치는 <?xml ...>와 루트 컨테이너 태그 사이이다.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.*?>
<루트 컨테이너 xmlns:fx="http://javafx.com/fxml">
...
</루트 컨테이너>
- import를 제대로 하지 않으면 not a valid type 메시지와 함께 javafx.fxml.LoadException 발생
태그(tag) 선언
- 시작 태그와 끝 태그가 매칭되어야 한다.
자바 코드 | FXML |
---|---|
Button button = new Button(); button.setText("확인"); |
<Button> <text>확인</text> </Button> |
속성(attribute) 선언
- 속성은 "나 '로 감싸야 한다.
<태그이름 속성명="값" 속성명='값'> ... </태그이름>
- 속성명은 Setter 메소드 명이 옴
- 모든 Setter가 사용될 수 있는 것은 아님. 기본 타입(boolean, byte, short, char, int, long, float, double)의 값을 세팅하거나, String을 세팅하는 Setter만 올 수 있음
- 예
자바 코드 | FXML (Setter 태그) | FXML (Setter 속성) |
---|---|---|
Button button = new Button(); button.setText("확인"); |
<Button> <text>확인</text> </Button> |
<Button text="확인"/> |
객체 선언
클래스 속성
- 생성자에 매개 변수가 있고, 매개 변수에 @NamedArg(javafx.beans.NamedArg) 어노테이션이 적용되어 있으면 속성명이나 자식 태그로 작성할 수 있음
<클래스 매개변수="값"> |
<클래스> <매개변수>값</매개변수> </클래스> |
- 예
- HBox를 패딩할 때 setPadding(Insets value) 메소드를 사용하는데
- Insets는 기본 생성자가 없고,
- Insets(double topRightBottomLeft) 또는 Insets(double top, double right, double bottom, double left)만 있음
- 이 경우 아래와 같이 선언 가능함
프로그램적 레이아웃 자바 코드 | FXML 레이아웃 태그 |
---|---|
HBox hbox = new HBox(); hbox.setPadding(new Inset(10, 10, 10, 10)); hbox.setSpacing(10); |
<HBox> <padding> <Insets top="10" right="10" bottom="10" left="10"/> </padding> </HBox> |
클래스 fx:value
- 클래스가 valueOf(String) 메소드를 제공하는 경우
<클래스 fx:value="값" />
기본 코드 | FXML |
---|---|
String.valueOf("Hello, World!"); Integer.valueOf("1"); Double.valueOf("1.0"); Boolean.valueOf("false"); |
<String fx:value="Hello, World!"/> <Integer fx:value="1"/> <Double fx:value="1.0"/> <Boolean fx:value="false"/> |
클래스 fx:constant
- 클래스에 정의된 상수값을 얻고 싶을 경우
<클래스 fx:constant="상수" />
기본 코드 | FXML |
---|---|
Button button = new Button(); button.setMaxWidth( Double.MAX_VALUE ); |
<Button> <maxWidth> <Double fx:constant="MAX_VALUE"/> </maxWidth> <Button> |
클래스 fx:factory
- 어떤 클래스는 new 연산자로 객체를 생성할 수 없고,
- 정적 메소드(이를 factory 메소드라 부른다)로 객체를 얻어야 하는 경우가 있음
<클래스 fx:factory="정적메소드" />
- 예:
ObservableList
의 구현 객체는javafx.collections.FXCollections
의 정적 메소드인observableArrayList(E... items)
메소드로 얻을 수 있다.
기본 코드 | FXML |
---|---|
ComboBox combo = new ComboBox(); combo.setItems(FXCollections.observableArrayList("공개", "비공개")); |
<ComboBox> <Items> <FXCollections fx:factory="observableArrayList"> <String fx:value="공개"/> <String fx:value="비공개"/> </FXCollections> </items> </ComboBox> |
FXML 로딩과 Scene 생성
- FXML 파일을 작성한 후 이를 이용하여 객체를 만들어야 한다. 이를 FXML loading이라고 한다.
- javafx.fxml.FXMLLoader를 이용
- 두 개의 load() 메소드 : 정적 메소드, 인스턴스 메소드
Parent root = FXMLLoader.load(getClass().getResource("xxx.fxml"));
- getClass() - 현재 클래스 리턴
- getResource() - 클래스가 위치하는 곳에서 상대 경로로 리소스의 URL을 리턴
- load() - FXML 파일을 로딩
FXMLLoader loader = new FXMLLoader(getClass().getResource("xxx.fxml"));
Parent root = (Parent)loader.load();
- load() - Parent 타입을 리턴함. 이것은 FXML 파일에서의 루트 태그로 선언된 컨테이너임
- 만을 루트 태그가 <HBox> 라면 다음과 같이 작성해도 됨
HBox hbox = (HBox) FXMLLoader.load(getClass().getResource("xxx.fxml"));
- 예제 코드
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("root.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("AppMain");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
JavaFX Scene Builder
- Scene Builder 다운로드 - https://gluonhq.com/products/scene-builder/
- SceneBuilder extension for Visual Studio Code
- vscode에서 Ctrl-Shift-P를 눌러 "Configure Scene Builder path"를 실행한다.
- 경로를 다음과 같이 지정한다 : C:\Users\profs\AppData\Local\SceneBuilder 폴더의 SceneBuilder.exe 지정
- vscode에서 Ctrl-Shift-P를 눌러 "Configure Scene Builder path"를 실행한다.
- Scene Builder 띄우는 방법
- fxml 파일을 선택한다.
- vscode에서 Ctrl-Shift-P를 누른다.
JavaFX 컨테이너
컨테이너 | 설명 |
---|---|
AnchorPane | 컨트롤을 좌표로 배치하는 레이아웃 |
BorderPane | 위, 아래, 오른쪽, 왼쪽, 중앙에 컨트롤을 배치하는 레이아웃 |
FlowPane | 행으로 배치하되 공간이 부족하면 새로운 행에 배치하는 레이아웃 |
GridPane | 그리드로 배치하되 셀의 크기가 고정적이지 않은 레이아웃 |
StackPane | 컨트롤을 겹쳐서 배치하는 레이아웃 |
TilePane | 그리드로 배치하되 고정된 셀의 크기를 갖는 레이아웃 |
HBox | 수평으로 배치하는 레이아웃 |
VBox | 수직으로 배치하는 레이아웃 |
AnchorPane 컨테이너
- AnchorPane에서 사용할 수 있는 주요 설정
태그 및 속성 | 설명 | 적용 |
---|---|---|
PrefWidth | 폭을 설정 | AnchorPane |
PrefHeight | 높이를 설정 | AnchorPane |
layoutX | 컨트롤의 X 좌표 | 컨트롤 |
layoutY | 컨트롤의 Y 좌표 | 컨트롤 |
<children> | 컨트롤을 포함 | AnchorPane |
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml" prefHeight="150.0" prefWidth="300.0">
<children>
<Label layoutX="42.0" layoutY="28.0" text="아이디" />
<Label layoutX="42.0" layoutY="66.0" text="패스워드" />
<TextField layoutX="120.0" layoutY="24.0" />
<PasswordField layoutX="120.0" layoutY="62.0" />
<Button layoutX="97.0" layoutY="106.0" text="로그인" />
<Button layoutX="164.0" layoutY="106.0" text="취소" />
</children>
</AnchorPane>
HBox와 VBox 컨테이너
태그 및 속성 | 설명 | 적용 |
---|---|---|
prefWidth | 폭을 설정 | HBox, VBox |
prefHeight | 높이를 설정 | HBox, VBox |
alignment | 컨트롤의 정렬을 설정 | HBox, VBox |
spacing | 컨트롤의 간격을 설정 | HBox, VBox |
fillWidth | 컨트롤의 폭 확장 여부 설정 | VBox |
fillHeight | 컨트롤의 높이 확장 여부 설정 | HBox |
<children> | 컨트롤을 포함 | HBox, VBox |
<HBox.hgrow> <Priority fx:constant="ALWAYS"/> </HBox.hgrow> |
HBox의 남은 폭을 채움 | 컨트롤 |
<VBox.vgrow> <Priority fx:constant="ALWAYS"/> </VBox.vgrow> |
VBox의 남을 높이를 채움 | 컨트롤 |
- root2.fxml
- 이 패키지의 하위 폴더로
images
를 만들고 여기에 javafx.png 파일을 복사해 넣어둔다.
- 이 패키지의 하위 폴더로
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.Double?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.Priority?>
<VBox xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
<children>
<ImageView fitWidth="200.0" preserveRatio="true"> <!-- 그림의 비율에 맞게 높이를 설정 -->
<image>
<Image url="@images/javafx.png" /> <!-- 현재 경로 기준으로 상대경로로 파일 지정 -->
</image>
</ImageView>
<HBox alignment="CENTER" spacing="20.0">
<children>
<Button text="이전" />
<Button text="다음">
<HBox.hgrow><Priority fx:constant="ALWAYS"/></HBox.hgrow> <!-- 오른쪽 남은 공간을 버튼이 모두 채우도록 설정 -->
<maxWidth><Double fx:constant="MAX_VALUE"/></maxWidth> <!-- 버튼의 폭을 자동으로 확장하기 위해 설정-->
</Button>
</children>
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
</HBox>
</children>
</VBox>
- 예제
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class AppMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("root2.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("AppMain");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
BorderPane 컨테이너
- top, bottom, left, right, center 셀에 컨트롤을 배치하는 컨테이너
- 다른 컨테이너를 배치할 수도 있음
- 각 셀에는 하나의 컨트롤 또는 컨테이너만 배치할 수 있음
태그 및 속성 | 설명 | 적용 |
---|---|---|
prefWidth | 폭을 설정 | BorderPane |
prefHeight | 높이를 설정 | BorderPane |
<top> | top에 배치될 컨트롤을 포함 | BorderPane |
<bottom> | bottom에 배치될 컨트롤을 포함 | BorderPane |
<right> | right에 배치될 컨트롤을 포함 | BorderPane |
<left> | left에 배치될 컨트롤을 포함 | BorderPane |
<center> | center에 배치될 컨트롤을 포함 | BorderPane |
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="200.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<top>
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<items>
<Button text="Button" />
<Button text="Button" />
</items>
</ToolBar>
</top>
<center>
<TextArea /> <!-- left와 right까지 확장-->
</center>
<bottom>
<BorderPane>
<center>
<TextField /> <!-- top, bottom, left까지 확장-->
</center>
<right>
<Button text="Button" />
</right>
</BorderPane>
</bottom>
</BorderPane>
FlowPane 컨테이너
태그 및 속성 | 설명 | 적용 |
---|---|---|
prefWidth | 폭을 설정 | FlowPane |
prefHeight | 높이를 설정 | FlowPane |
hgap | 컨트롤의 수평 간격을 설정 | FlowPane |
vgap | 컨트롤의 수직 간격을 설정 | FlowPane |
<children> | 컨트롤을 포함 | FlowPane |
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.FlowPane?>
<FlowPane prefHeight="70.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
<children>
<Button text="Button" />
<Button text="Button" />
<Button text="Button" />
<Button text="Button" />
<Button text="Button" />
<Button text="Button" />
</children>
</FlowPane>
TilePane 컨테이너
태그 및 속성 | 설명 | 적용 |
---|---|---|
prefWidth | 폭을 설정 | TilePane |
prefHeight | 높이를 설정 | TilePane |
prefTileWidth | 타일의 폭을 설정 | TilePane |
prefTileHeight | 타일의 높이를 설정 | TilePane |
<children> | 컨트롤을 포함 | TilePane |
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.TilePane?>
<TilePane xmlns:fx="http://javafx.com/fxml" prefHeight="100.0" prefWidth="100.0">
<children>
<ImageView>
<image><Image url="@images/fruit1.jpg" /></image>
</ImageView>
<ImageView>
<image><Image url="@images/fruit2.jpg" /></image>
</ImageView>
<ImageView>
<image><Image url="@images/fruit3.jpg" /></image>
</ImageView>
<ImageView>
<image><Image url="@images/fruit4.jpg" /></image>
</ImageView>
<ImageView>
<image><Image url="@images/fruit5.jpg" /></image>
</ImageView>
</children>
</TilePane>
GridPane 컨테이너
태그 및 속성 | 설명 | 적용 |
---|
StackPane 컨테이너
태그 및 속성 | 설명 | 적용 |
---|