컴퓨터프로그래밍및실습 (2022년)/1128

From DISLab
Jump to navigation Jump to search

JavaFX 개요

  1. AWT
    • Native UI 컴포넌트 사용
    • 운영체제 마다 UI 모양이 다름
  2. Swing
    • 운영체제가 제공하는 native UI 사용 안 함
    • 운영체제가 새롭게 제공하는 UI 지원의 어려움
  3. 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은 직접 생성해야 함
AppMain 실행화면
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)
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)

구분 HBox의 패딩 Button의 마진
개념 CP-17.3.3-padding.png CP-17.3.3-margin.png
자바 코드 HBox hbox = new HBox();
hbox.setPadding(new Insets(50));
Button button = new Button();
HBox.setMargin(button, new Insets(50));
FXML 태그 <HBox>
<padding>
<Insets topRightBottomLeft="50"/>
</padding>
</HBox>
<Button>
<HBox.margin>
<Insets topRightBottomLeft="50"/>
</Hbox.margin>
</Button>

  • 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>

JavaFX 컨테이너

JavaFX 이벤트 처리

JavaFX 속성 감시와 바인딩

JavaFX 컨트롤

JavaFX 메뉴바(MenuBar)와 툴바(Toolbar)

JavaFX 다이얼로그(Dialog)

JavaFX CSS 스타일

JavaFX 스레드 동시성

화면 이동과 애니메이션