1. 소개

JavaFX는 Java로 리치 클라이언트 애플리케이션을 빌드하기 위한 라이브러리입니다. Java를 지원하는 거의 모든 장치에서 실행되는 GUI 응용 프로그램을 설계하기 위한 API를 제공합니다 .

이 사용방법(예제)에서는 몇 가지 주요 기능과 기능에 중점을 두고 다룹니다.

2. 자바FX API

Java 8, 9 및 10에서는 JavaFX 라이브러리 작업을 시작하기 위해 추가 설정이 필요하지 않습니다. 프로젝트는 JDK 11부터 JDK에서 제거됩니다.

2.1. 건축학

JavaFX는 Prism으로 알려진 렌더링을 위해 하드웨어 가속 그래픽 파이프라인을 사용합니다 . 또한 그래픽 사용을 완전히 가속화하기 위해 내부적으로 DirectXOpenGL을 사용하여 소프트웨어 또는 하드웨어 렌더링 메커니즘을 활용합니다 .

JavaFX에는 기본 운영 체제에 연결하기 위한 플랫폼 종속 Glass 창 도구 키트 계층이 있습니다 . 운영 체제의 이벤트 대기열을 사용하여 스레드 사용을 예약합니다. 또한 창, 이벤트, 타이머를 비동기식으로 처리합니다.

미디어 엔진은 미디어와 HTML / CSS 지원을 재생할 수 있습니다.

JavaFX 응용 프로그램 주요 구조 는 다음과 같습니다.

 

여기에서 두 가지 주요 컨테이너를 확인할 수 있습니다.

  • Stage 는 애플리케이션의 주요 컨테이너이자 진입점입니다 . 기본 창을 나타내며 start() 메서드의 인수로 전달됩니다.
  • Scene 은 Image Views, Buttons, Grids, TextBoxes와 같은 UI 요소를 담기 위한 컨테이너입니다.

장면 교체하거나 서로 전환 될 수 장면 . 이것은 장면 그래프 로 알려진 계층적 개체의 그래프를 나타냅니다 . 해당 계층의 각 요소를 노드라고 합니다. 단일 노드에는 ID, 스타일, 효과, 이벤트 핸들러, 상태가 있습니다.

또한 Scene 에는 레이아웃 컨테이너, 이미지, 미디어도 포함됩니다.

2.2. 스레드

시스템 수준 에서 JVM은 애플리케이션을 실행하고 렌더링하기 위해 별도의 스레드를 생성합니다 .

  • 프리즘 렌더링 스레드 – 장면 그래프를 별도로 렌더링하는 역할을 합니다.
  • 응용 프로그램 스레드 - 모든 JavaFX 응용 프로그램의 기본 스레드입니다. 모든 라이브 노드와 구성 요소가 이 스레드에 연결됩니다.

2.3. 수명 주기

javafx.application.Application의 클래스는 다음과 같은 라이프 사이클 메소드가 있습니다

  • init() – 애플리케이션 인스턴스가 생성된 후 호출 됩니다. 이 시점에서 JavaFX API는 아직 준비되지 않았으므로 여기에서 그래픽 구성 요소를 만들 수 없습니다.
  • start(Stage stage) – 모든 그래픽 구성 요소가 여기에서 생성됩니다. 또한 그래픽 활동의 메인 스레드는 여기에서 시작됩니다.
  • stop() – 애플리케이션이 종료되기 전에 호출됩니다. 예를 들어 사용자가 기본 창을 닫을 때입니다. 애플리케이션 종료 전에 일부 정리를 위해 이 메서드를 재정의하는 것이 유용합니다.

정적 launch() 메서드는 JavaFX 응용 프로그램을 시작합니다.

2.4. FXML

JavaFX는 특별한 FXML 마크업 언어를 사용하여 뷰 인터페이스를 생성합니다.

이것은 비즈니스 로직에서 뷰를 분리하기 위한 XML 기반 구조를 제공합니다. XML은 장면 그래프 계층 구조 를 아주 자연스럽게 나타낼 수 있으므로 여기에 더 적합 합니다.

마지막으로,로드 할 .fxml의 파일을, 우리는 사용 FXMLLoader의 장면 계층 구조의 객체 그래프에서 클래스, 결과를.

3. 시작하기

실용적으로 사용하기 위해 사람들 List을 검색할 수 있는 작은 응용 프로그램을 빌드해 보겠습니다.

먼저 Person 모델 클래스를 추가하여 도메인을 나타냅니다.

public class Person {
    private SimpleIntegerProperty id;
    private SimpleStringProperty name;
    private SimpleBooleanProperty isEmployed;

    // getters, setters
}

int, Stringboolean을 래핑하기 위해 javafx.beans.property 패키지 에서 SimpleIntegerProperty, SimpleStringProperty, SimpleBooleanProperty 클래스를 사용하는 방법에 주목하십시오 .

다음으로 Application 추상 클래스 를 확장하는 Main 클래스를 생성해 보겠습니다 .

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(
          Main.class.getResource("/SearchController.fxml"));
        AnchorPane page = (AnchorPane) loader.load();
        Scene scene = new Scene(page);

        primaryStage.setTitle("Title goes here");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

우리의 메인 클래스 는 프로그램의 진입점인 start() 메서드를 재정의합니다 .

그런 다음 FXMLLoaderSearchController.fxml 에서 AnchorPane 으로 개체 그래프 계층을 로드합니다 .

Scene을 시작한 후 기본 Stage로 설정합니다 . 또한 창의 제목을 설정하고 표시() 합니다.

JavaFX Launcher 없이 JAR 파일을 실행할 수 있도록 main() 메소드 를 포함하는 것이 유용합니다 .

3.1. FXML 보기

이제 SearchController XML 파일에 대해 자세히 살펴보겠습니다 .

검색 애플리케이션의 경우 키워드와 검색 버튼을 입력하는 텍스트 필드를 추가합니다.

<AnchorPane 
  xmlns:fx="http://javafx.com/fxml"
  xmlns="http://javafx.com/javafx"
  fx:controller="com.baeldung.view.SearchController">
    <children>

        <HBox id="HBox" alignment="CENTER" spacing="5.0">
            <children>
                <Label text="Search Text:"/>
                <TextField fx:id="searchField"/>
                <Button fx:id="searchButton"/>
            </children>
        </HBox>

        <VBox fx:id="dataContainer"
              AnchorPane.leftAnchor="10.0"
              AnchorPane.rightAnchor="10.0"
              AnchorPane.topAnchor="50.0">
        </VBox>

    </children>
</AnchorPane>

AnchorPane 은 여기에서 루트 컨테이너이며 그래프 계층의 첫 번째 노드입니다. 창의 크기를 조정하는 동안 해당 앵커 포인트로 자식의 위치가 변경됩니다. FX : 컨트롤러 속성 와이어 마크 업으로 자바 클래스입니다.

사용 가능한 다른 내장 레이아웃이 있습니다.

  • BorderPane – 레이아웃을 위쪽, 오른쪽, 아래쪽, 왼쪽, 중앙의 5개 섹션으로 나눕니다.
  • HBox – 수평 패널에 자식 구성 요소를 정렬합니다.
  • VBox – 자식 노드가 세로 열에 정렬됩니다.
  • GridPane – 행과 열이 있는 그리드를 만드는 데 유용합니다.

이 예에서는 수평 HBox 패널 내부에 Label사용하여 텍스트를 배치하고 TextField 를 입력에 사용하고 Button을 사용했습니다 . FX : 아이디 우리는 자바 코드에서 나중에 사용할 수 있도록 우리는 요소를 표시합니다.

의 VBox 우리는 검색 결과를 표시 볼 수있는 곳 패널입니다.

그런 다음 Java 필드에 매핑하기 위해 @FXML 어노테이션을 사용합니다 .

public class SearchController {
 
    @FXML
    private TextField searchField;
    @FXML
    private Button searchButton;
    @FXML
    private VBox dataContainer;
    @FXML
    private TableView tableView;
    
    @FXML
    private void initialize() {
        // search panel
        searchButton.setText("Search");
        searchButton.setOnAction(event -> loadData());
        searchButton.setStyle("-fx-background-color: #457ecd; -fx-text-fill: #ffffff;");

        initTable();
    }
}

@FXML 어노테이션 필드를 채우고 나면 initialize() 가 자동으로 호출됩니다. 여기에서 이벤트 리스너 등록, 스타일 추가 또는 텍스트 속성 변경과 같은 UI 구성 요소에 대한 추가 작업을 수행할 수 있습니다.

에서 initTable () 방법 우리는 3 열이 결과를 포함하고에 추가 할 테이블을 만듭니다 dataContainer 중 VBOX를 :

private void initTable() {        
    tableView = new TableView<>();
    TableColumn id = new TableColumn("ID");
    TableColumn name = new TableColumn("NAME");
    TableColumn employed = new TableColumn("EMPLOYED");
    tableView.getColumns().addAll(id, name, employed);
    dataContainer.getChildren().add(tableView);
}

마지막으로 여기에 설명된 이 모든 논리는 다음 창을 생성합니다.

4. 바인딩 API

이제 시각적 측면이 처리되었으므로 바인딩 데이터를 살펴보겠습니다.

바인딩 API는 다른 개체의 값 변경이 발생할 때 개체에 알리는 일부 인터페이스를 제공합니다.

bind() 메서드를 사용하거나 리스너를 추가 하여 값을 바인딩할 수 있습니다 .

단방향 바인딩은 한 방향에 대해서만 바인딩을 제공합니다.

searchLabel.textProperty().bind(searchField.textProperty());

여기에서 검색 필드를 변경하면 레이블의 텍스트 값이 업데이트됩니다.

이에 비해 양방향 바인딩은 두 속성의 값을 양방향으로 동기화합니다.

필드를 바인딩하는 다른 방법은 ChangeListeners입니다.

searchField.textProperty().addListener((observable, oldValue, newValue) -> {
    searchLabel.setText(newValue);
});

관찰 가능한 인터페이스 변경을위한 객체의 값을 관찰 할 수 있습니다.

이를 예시하기 위해 가장 일반적으로 사용되는 구현은 javafx.collections.ObservableList<T> 인터페이스입니다.

ObservableList<Person> masterData = FXCollections.observableArrayList();
ObservableList<Person> results = FXCollections.observableList(masterData);

여기서 요소의 삽입, 업데이트 또는 제거와 같은 모델 변경은 UI 컨트롤에 즉시 알립니다.

masterData의 List 초기 List을 포함합니다 사람이 개체 및 결과 List은 우리가 검색에 표시 List이 될 것입니다.

또한 테이블의 데이터를 초기 List에 바인딩하고 각 열을 Person 클래스 필드 에 연결하기 위해 initTable() 메서드 를 업데이트해야 합니다.

private void initTable() {        
    tableView = new TableView<>(FXCollections.observableList(masterData));
    TableColumn id = new TableColumn("ID");
    id.setCellValueFactory(new PropertyValueFactory("id"));
    TableColumn name = new TableColumn("NAME");
    name.setCellValueFactory(new PropertyValueFactory("name"));
    TableColumn employed = new TableColumn("EMPLOYED");
    employed.setCellValueFactory(new PropertyValueFactory("isEmployed"));

    tableView.getColumns().addAll(id, name, employed);
    dataContainer.getChildren().add(tableView);
}

5. 동시성

장면 그래프에서 UI 구성 요소로 작업하는 것은 응용 프로그램 스레드에서만 액세스하기 때문에 스레드로부터 안전하지 않습니다. javafx.concurrent의 패키지는 멀티 스레딩에 도움이 여기에있다.

백그라운드 스레드에서 데이터 검색을 수행하는 방법을 살펴보겠습니다.

private void loadData() {
    String searchText = searchField.getText();
    Task<ObservableList<Person>> task = new Task<ObservableList<Person>>() {
        @Override
        protected ObservableList<Person> call() throws Exception {
            updateMessage("Loading data");
            return FXCollections.observableArrayList(masterData
                    .stream()
                    .filter(value -> value.getName().toLowerCase().contains(searchText))
                    .collect(Collectors.toList()));
        }
    };
}

여기에서 일회성 작업 javafx.concurrent.Task 객체를 만들고 call() 메서드를 재정의합니다 .

호출 () 메서드는 백그라운드 스레드에서 완전히 실행하고 응용 프로그램 스레드로 결과를 반환합니다. 즉, 이 메서드 내에서 UI 구성 요소를 조작하면 런타임 예외가 발생합니다.

그러나 updateProgress(), updateMessage() 를 호출하여 애플리케이션 스레드 항목을 업데이트할 수 있습니다. 작업 상태가 SUCCEEDED 상태로 전환되면 onSucceeded() 이벤트 핸들러가 Application 스레드에서 호출됩니다.

task.setOnSucceeded(event -> {
    results = task.getValue();
    tableView.setItems(FXCollections.observableList(results));
});

동일한 콜백에서 tableView 데이터를 새로운 결과 List으로 업데이트했습니다 .

태스크 입니다 Runnable를 그래서를 시작하는 데, 우리는 새로운 시작에 불과 필요 스레드작업 매개 변수를 :

Thread th = new Thread(task);
th.setDaemon(true);
th.start();

setDaemon를 (참) 플래그는 스레드가 작업을 마무리 한 후 종료됩니다 것을 나타냅니다.

6. 이벤트 처리

이벤트를 애플리케이션에 흥미로울 수 있는 작업으로 설명할 수 있습니다.

예를 들어, 마우스 클릭, 키 누름, 창 크기 조정과 같은 사용자 동작은 javafx.event.Event 클래스 또는 그 하위 클래스에 의해 처리되거나 통지됩니다 .

또한 세 가지 유형의 이벤트를 구분합니다.

  • InputEventKEY_PRESSED, KEY_TYPED, KEY_RELEASED 또는 MOUSE_PRESSES, MOUSE_RELEASED 와 같은 모든 유형의 키 및 마우스 동작
  • ActionEventButton 실행 또는 KeyFrame 종료 와 같은 다양한 작업을 나타냅니다.
  • 창 이벤트WINDOW_SHOWING, WINDOW_SHOWN

시연하기 위해 아래 코드 조각 searchField 위에서 Enter를 누르는 이벤트를 포착합니다 .

searchField.setOnKeyPressed(event -> {
    if (event.getCode().equals(KeyCode.ENTER)) {
        loadData();
    }
});

7. 스타일

We can change the UI of the JavaFX application by applying a custom design to it.

By default, JavaFX uses modena.css as a CSS resource for the whole application. This is a part of the jfxrt.jar.

To override the default style, we can add a stylesheet to the scene:

scene.getStylesheets().add("/search.css");

We can also use inline style; for example, to set a style property for a specific node:

searchButton.setStyle("-fx-background-color: slateblue; -fx-text-fill: white;");

8. Conclusion

This brief write-up covers the basics of JavaFX API. We went through the internal structure and introduced key capabilities of its architecture, lifecycle, and components.

As a result, we learned and are now able to create a simple GUI application.

And, as always, the full source code of the tutorial is available over on GitHub.

Junit footer banner