티스토리 뷰

들어가며

  • 이전에 썼던 글인 JavaFX TableView 사용법 이 시간이 지나서 보니 조금 설명이 허접한 것 같기도 하고.. 리뉴얼이 필요할 것 같았다.
 

[JavaFX] Table View 사용법

Table View 사용법 이번 시간에는 JavaFX의 Table View 사용법에 대해서 알아보도록 하겠습니다. 일단 Table View를 fxml코드에서 생성해보겠습니다. ■ TableView 생성 * 참고로 모든 소스파일들은 하나의 안에..

jinseongsoft.tistory.com

  • 원본 소스는 아래 위치에 있습니다.
 

rlawlstjd0077/JavaFxPlayGround

Contribute to rlawlstjd0077/JavaFxPlayGround development by creating an account on GitHub.

github.com


TableView

    • TableView 데이터를 Row과, Column형태로 시각화하도록 설계된 컨트롤이다.

      • 얼핏보면 ListView와 비슷하지만 Column이 추가되었다는 점에서 다름
    • TableView의 특징

      • 강력한 TableColumn API를 제공함 (TableColumn은 아래에서 자세하게 설명하도록 하겠음)
        • Cell을 쉽게 customize할 수 있는 Cell Factory들을 제공함 (Cell은 TableView의 한 칸이라고 보면 될 것 같음)
        • minWidth / prefWidth / maxWidth 및 fixed width를 Column에 적용 가능함
        • 런타임시에 리사이징이 가능함
        • 런타임시에 Column 렌더링이 가능함
        • Column 중첩을 지원함
      • 사용자가 Column 크기를 조정할 때 발생하는 UI 동작에 대한 다양한 리사이징 정책이 있음
      • Column Header를 클릭했을 때 다양한 데이터 정렬을 지원함 (여러 열을 한번에 정렬하기 위해서는 Shift를 누른채로 클릭하길)
    • TableView는 데이터를 시각화하기 위한 의도로 만들어진 컨트롤이다.

      • 사용자 인터페이스를 배치하는 데 사용되지 않음
      • 만약 Grid 형태로 사용자 인터페이스를 배치하고자 한다면 GridPane 레이아웃 사용을 고려해보길
    • 설명은 했지만 잘 이해가 안될 수도 있다. 아마 사용하다 보면 이해가 될 것이다.


TableColumn

  • TableView는 다수의 TableColumn으로 이루어져 있다.

  • TableColumn은 단일 열에 대한 데이터를 표시하고 편집하며 이에 필요한 속성도 포함한다.

    • 크기 조정 (minWidth / prefWidth / maxWidth 및 width 속성 사용)
    • visibility 전환
    • Header Text 전시
    • 중첩 Column이 포함된 경우 표시
    • Column 우클릭 시 전시되는 메뉴
    • 테이블의 내용 정렬
  • TableColumn을 생성할 때 가장 중요한 속성 두 가지는

    • Column Header에 표시될 Text를 정의하는 것
      ObservableList<Person> data = ...
      TableView<Person> tableView = new TableView<Person>(data);
    • Column의 cell value factory를 정의하는 것
      TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
      firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
        public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
           // p.getValue() returns the Person instance for a particular TableView row
           return p.getValue().firstNameProperty();
        }
      });
      
      • 이 코드에서 중요한 부분은 p.getValue().firstNameProperty()
        • p.getValue()는 Person 인스턴스인데 firstNameProperty()는 아마 String을 감싸고 있는 StringProperty 일 것임
      • 이 코드의 이점은 외부에 있을 Person 인스턴스의 firstName 값이 변경되면 자동으로 TableView의 해당 Cell의 데이터도 같이 변경된다는 것임
        • 그 이유는 StringProperty가 TableView에 바인딩이 되어 감싸고 있는 값이 바뀌면 바인딩이 된 TableView에도 값이 바뀌는 것임

TableView 만들어보기

  • 지루한 설명이 끝났다. 이제 직접 한번 만들어보자

1. FXML

Table.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<fx:root style="-fx-background-color: #A2A5AC;" type="AnchorPane" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" stylesheets="@../../fx.css" prefWidth="800">
  <TableView fx:id="tableView" focusTraversable="false"  AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0" AnchorPane.rightAnchor="20.0" AnchorPane.topAnchor="20.0">
    <placeholder><Label text="No Content" /></placeholder>
  </TableView>
</fx:root>
  • <fx:root>가 뭔지 궁금하다면 이 글을 참고
  • CSS는 이 곳을 참고

2. Data 클래스

  • TableView에 전시될 Data 클래스를 만들어보자.

TableData.java

public class TableData {
  private String id;
  private ZonedDateTime dateTime;
  private String content;
  private boolean isButtonVisible;

  public TableData(String id, ZonedDateTime dateTime, String content, boolean isButtonVisible) {
    ... 생략 ...
  }

  public String getId() { return id;}
  public ZonedDateTime getDateTime() { return dateTime; }
  public String getContent() { return content; }
  public boolean isButtonVisible() { return isButtonVisible; }
}
  • 만약 데이터가 변경되었을 때 TableView에도 적용되길 원한다면 Property로 이루어진 아래 코드를 쓰자.

TableDataWithProperty.java

public class TableDataWithProperty {
  private StringProperty id;
  private ObjectProperty<ZonedDateTime> dateTime;
  private StringProperty content;
  private BooleanProperty isButtonVisible;

  public TableDataWithProperty(StringProperty id, ObjectProperty<ZonedDateTime> dateTime, StringProperty content, BooleanProperty isButtonVisible) {
    this.id = id;
    this.dateTime = dateTime;
    this.content = content;
    this.isButtonVisible = isButtonVisible;
  }

  public TableDataWithProperty(String id, ZonedDateTime dateTime, String content, boolean isButtonVisible) {
    this.id.set(id);
    this.dateTime.set(dateTime);
    this.content.set(content);
    this.isButtonVisible.set(isButtonVisible);
  }

  public StringProperty idProperty() { return id;}
  public ObjectProperty<ZonedDateTime> dateTimeProperty() { return dateTime; }
  public StringProperty contentProperty() { return content; }
  public BooleanProperty isButtonVisibleProperty() { return isButtonVisible; }
}

3. Display 클래스

  • 이 클래스는 TableView에 Display되는 기능을 담당하기 위해 분리된 클래스이다.
    • 쉽게 말해서 TableColumn을 정의하기 위한 클래스로 알아두면 좋을 것 같음
  • 아래 코드의 형태는 TableColumn을 정의한 뒤에 TableView에 Data가 삽입되었을 때 Data의 어떤 값을 Column에 전시할 것인지를 정의한다.

DisplayTableData.java

public class DisplayTableData {

  public TableColumn[] getColumns() {
    // ID 칼럼
    final TableColumn<TableData, String> idColumn = new TableColumn<>("ID");
    idColumn.setCellValueFactory(item -> new ReadOnlyStringWrapper(item.getValue().getId()));
    idColumn.setPrefWidth(40);

    //Content 칼럼
    final TableColumn<TableData, String> contentColumn = new TableColumn<>("Content");
    contentColumn.setCellValueFactory(item -> new ReadOnlyStringWrapper(item.getValue().getContent()));
    contentColumn.setPrefWidth(10);

    //DateTime 칼럼
    final TableColumn<TableData, String> dateTimeColumn = new TableColumn<>("DateTime");
    dateTimeColumn.setCellValueFactory(item -> new ReadOnlyStringWrapper(
        //ZonedDateTime Formatting 하여 전시 (예: 2020-02-01 13:00:11.22)
        item.getValue().getDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))));
    dateTimeColumn.setPrefWidth(220);

    //Action 칼럼`
    final TableColumn<TableData, Boolean> actionColumn = new TableColumn<>("Action");
    actionColumn.setCellValueFactory(item -> new ReadOnlyBooleanWrapper(item.getValue().isButtonVisible()));
    actionColumn.setCellFactory(item -> new TableCell<TableData, Boolean>() {
      @Override
      protected void updateItem(Boolean isButtonVisible, boolean empty) {
        super.updateItem(isButtonVisible, empty);
        if (!empty && isButtonVisible) {
          Button actionButton = new Button("Action");
          actionButton.setOnAction(event -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.getDialogPane().setPrefWidth(200);
            alert.setTitle("Test");
            alert.setHeaderText("Action button clicked!");
            alert.showAndWait();
          });
          setGraphic(actionButton);
        }
      }
    });
    contentColumn.setPrefWidth(200);

    return new TableColumn[]{idColumn, contentColumn, dateTimeColumn, actionColumn};
  }

}
  • 만약 TableDataWithProperty를 사용했다면 아래 코드 참고

DisplayTableDataWithProperty.java

public class DisplayTableDataWithProperty {
  public TableColumn[] getColumns() {
    // ID 칼럼
    final TableColumn<TableDataWithProperty, String> idColumn = new TableColumn<>("ID");
    idColumn.setCellValueFactory(item -> item.getValue().idProperty());
    idColumn.setPrefWidth(40);

    //Content 칼럼
    final TableColumn<TableDataWithProperty, String> contentColumn = new TableColumn<>("Content");
    idColumn.setCellValueFactory(item -> item.getValue().contentProperty());
    contentColumn.setPrefWidth(10);

    //DateTime 칼럼
    final TableColumn<TableDataWithProperty, ZonedDateTime> dateTimeColumn = new TableColumn<>("DateTime");
    dateTimeColumn.setCellValueFactory(item -> item.getValue().dateTimeProperty());
    dateTimeColumn.setCellFactory(col -> new TableCell<TableDataWithProperty, ZonedDateTime>() {
      @Override
      protected void updateItem(ZonedDateTime item, boolean empty) {
        super.updateItem(item, empty);
        if (empty) {
          setText(null);
        } else {
          setText(item.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        }
      }
    });
    dateTimeColumn.setPrefWidth(220);

    //Action 칼럼`
    final TableColumn<TableDataWithProperty, Boolean> actionColumn = new TableColumn<>("Action");
    actionColumn.setCellValueFactory(item -> item.getValue().isButtonVisibleProperty());
    actionColumn.setCellFactory(item -> new TableCell<TableDataWithProperty, Boolean>() {
      @Override
      protected void updateItem(Boolean isButtonVisible, boolean empty) {
        super.updateItem(isButtonVisible, empty);
        if (!empty && isButtonVisible) {
          Button actionButton = new Button("Action");
          actionButton.setOnAction(event -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.getDialogPane().setPrefWidth(200);
            alert.setTitle("Test");
            alert.setHeaderText("Action button clicked!");
            alert.showAndWait();
          });
          setGraphic(actionButton);
        }
      }
    });
    contentColumn.setPrefWidth(200);

    return new TableColumn[]{idColumn, contentColumn, dateTimeColumn, actionColumn};
  }
}

4. TableController 클래스

  • Display클래스를 통해서 TableColumn을 세팅하고 Data를 받아 TableView에 전시하는 형태이다.
  • FxUtil.loadFxml(this)는 FXML 파일을 로딩하는 코드를 Util 클래스로 분리한 것으로 이 곳을 참고

TableController.java

public class TableController extends AnchorPane {

  @FXML
  private TableView<TableData> tableView;

  public TableController(List<TableData> tableDataList) {
    FxUtil.loadFxml(this);
    tableView.getColumns().setAll(new DisplayTableData().getColumns());
    tableView.getItems().setAll(tableDataList);
  }
}

5. Test 클래스

TableTest.java

public class TableTest extends Application {

  @Test
  public void test() {
    launch();
  }

  private List<TableData> generateTableData() {
    List<TableData> tableDataList = new ArrayList<>();
    tableDataList.add(new TableData("1",  ZonedDateTime.now().minusSeconds(1), "I'm 1", true));
    tableDataList.add(new TableData("2",  ZonedDateTime.now().minusSeconds(2), "I'm 2", false));
    tableDataList.add(new TableData("3",  ZonedDateTime.now().minusSeconds(3), "I'm 3", true));
    tableDataList.add(new TableData("4",  ZonedDateTime.now().minusSeconds(4), "I'm 4", false));
    tableDataList.add(new TableData("5",  ZonedDateTime.now().minusSeconds(5), "I'm 5", true));
    tableDataList.add(new TableData("6",  ZonedDateTime.now().minusSeconds(6), "I'm 6", true));
    tableDataList.add(new TableData("7",  ZonedDateTime.now().minusSeconds(7), "I'm 7", false));
    tableDataList.add(new TableData("8",  ZonedDateTime.now().minusSeconds(8), "I'm 8", false));
    tableDataList.add(new TableData("9",  ZonedDateTime.now().minusSeconds(9), "I'm 9", true));
    return tableDataList;
  }

  @Override
  public void start(Stage primaryStage) throws IOException {
    TableController controller = new TableController(generateTableData());
    primaryStage.setScene(new Scene(controller));
    primaryStage.setTitle("Table Test");
    primaryStage.show();
  }
}

결과

  • TableView가 전시되는 것을 볼 수 있다. (디자인은 신경쓰지 않겠다)

  • Action 버튼을 클릭하면 Alert이 잘 전시되는 것을 볼 수 있다.


관련 글

 

[JavaFX] TableView Header 없애는 방법

JavaFX Table View Header 없애기 이번 시간에는 TableView의 Header를 없애는 방법을 알아보겠습니다. TableView의 사용법은 이전 포스트를 참고하시면 도움이 될 것 입니다. 방법은 매우 간단합니다. Controller..

jinseongsoft.tistory.com

 

[JavaFX] TableView 이벤트 처리

JavaFX TableView 이벤트 처리 이번 시간에는 JavaFX TableView의 이벤트 처리 방법에 대해서 알아보도록 하겠습니다. TableView 사용법은 이전 포스트 를 참고하시면 됩니다. TableView가 만들어졌다는 가정하에..

jinseongsoft.tistory.com

 

[JavaFX] TableView 응용 ( TableView에 동적으로 데이터 추가 해보기 )

TableView에 동적으로 데이터 추가 하기 이번 시간에는 TableView의 응용으로 간단하게 TextField에 데이터를 입력하고 TableView에 데이터를 추가하는 방법에 대해서 알아보도록 하겠습니다. TableView의 사용법..

jinseongsoft.tistory.com

 

[JavaFX] TableView 두번 Select 됬을 때 Select 해제하기(SelectionModel 수정)

TableView Select 두번 시 해제하기 이번 시간에는 JavaFX TableView의 Select 된 row를 한번더 Select 했을 때 해제 하는 방법을 알아보겠습니다. 뭔 소린지 이해가 가지 않으실 것 같아서 결과화면을 미리 준비..

jinseongsoft.tistory.com


끝으로

이 글이 도움이 되었다면, 하단의 Google 광고 👎👎👎 한번씩 클릭 부탁 드립니다. 🙏🙏🙏

광고 클릭은 많은 힘이 됩니다! 

반응형
댓글