티스토리 뷰
JavaFX TreeView 다루는 법
TreeItem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class MyTreeView extends TreeView<String> { private String rootValue = "Root"; private String subRootValue = "Sub Root"; private String DEFAULT_STYLE_CLASS = "my-tree-view"; private String CSS_PATH = "treeview.css"; private String[] rootItem = {"1", "2", "3"}; private Image normalRootIcon; private Image focusedRootIcon; private Image normalChildIcon; private Image focusedChildIcon; public MyTreeView() { Image normalRootIcon = new Image(getClass().getResourceAsStream("normalRoot.png")); Image focusedRootIcon = new Image(getClass().getResourceAsStream("focusedRoot.png"));; Image normalChildIcon = new Image(getClass().getResourceAsStream("normalChild.png"));; Image focusedChildIcon = new Image(getClass().getResourceAsStream("focusedChild.png"));; getStylesheets().add(CSS_PATH); getStyleClass().add(DEFAULT_STYLE_CLASS); setPrefWidth(200); setPrefHeight(200); TreeItem<String> root = new TreeItem(rootValue, new ImageView(normalRootIcon)); TreeItem<String> subRoot = new TreeItem(subRootValue, new ImageView(normalRootIcon)); root.setExpanded(true); subRoot.setExpanded(true); for (String itemString : rootItem) { subRoot.getChildren().add(new TreeItem(itemString, new ImageView(normalChildIcon)); } root.getChildren().add(subRoot); //this.setCellFactory(treeView -> new MyTreeCell()); this.setRoot(root); } } | cs |
CSS 파일을 파일로 다운받으시기 바랍니다. my-tree-view.css
이렇게 만들게 되면 효과는 제외하고 원하는 결과를 얻을 수 있습니다. 아래와 같이 말이죠.
그런데 문제는 이제 효과 입니다. 효과를 어떻게 넣어줄까요..
그 전에 TreeItem 이 도대체 뭔지 알아보죠.
TreeItem 이란?
- TreeView와 같은 컨트롤에 값의 계층 구조를 제공하는 단일 노드 모델이다.
- 이 모델에서는 항목 수가 변경되거나 위치 자체가 변경됬을 때 알림을 받을 수 있는 리스너 등록을 허용한다.
- 명심해야 할 것은 TreeItem은 노드가 아니다. 그렇기 때문에 시각적인 이벤트가 발생하지는 않는다.
- 그래서 이벤트를 얻으려면 리스너를 TreeCell 인스턴스의 이벤트에 추가를 해줘야 한다.
3, 4 번을 주목해 보시면 TreeItem은 노드가 아니기 때문에 이벤트가 발생하지 않습니다.
그렇기 때문에 TreeCell 이라는 것을 사용해야 합니다.
TreeCell
위 코드에서 33번 Line에 주석 처리가 되있는 부분이 보이시나요? 이 부분이 바로 TreeItem들을 TreeCell 의 인스턴스로 추가하는 것입니다.
MyTreeCell 이라는 클래스를 별도로 만들겠습니다.
MyTreeCell.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class MyTreeCell extends TreeCell<String> { public MyTreeCell() { //이벤트 처리 } @Override protected void updateItem(String item, boolean empty) { super.updateItem(item, empty); if(!empty){ setText(item); TreeItem treeItem = getTreeItem(); setGraphic(new ImageView(treeItem.getGraphic())); }else{ setText(null); setGraphic(null); } } } | cs |
위 코드 에서는 updateItem 이라는 메소드를 통해서 TreeView의 모든 Item들을 TreeCell 로 초기화 해주는 작업을 진행합니다.
empty에는 해당 item이 비어있는지 여부를 나타내는데, 이를 통해서는 TreeView의 비어있는 Cell들 까지도 체크를 하는 것을 알 수 있죠.
그래서 만들어진 Item 하나하나의 String 값과 ImageView들을 가져와서 설정을 해줍니다.
그런데 사실 생각해보면 item이 root인지 아닌지 검사할 수 있는 방법은 지금은 존재 하지 않습니다. cell로 바뀌면서 말이죠.
그래서 이 과정을 좀 더 편하게 하기 위해서 TreeItem을 직접 구현해 보도록 하겠습니다.
TreeItem 구현
아래의 코드는 처럼 TreeItem을 상속받은 클래스를 만들겠습니다.
MyTreeItem.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class MyTreeItem extends TreeItem<String> { private Image normalIcon; private Image focusedIcon; public MyTreeItem(String text, String normalIconPath, String focusedIconPath) { super(text); this.normalIcon = new Image(getClass().getResourceAsStream(normalIconPath)); this.focusedIcon = new Image(getClass().getResourceAsStream(focusedIconPath)); setGraphic(new ImageView(this.normalIcon)); } public Image getNormalIcon() { return normalIcon; } public Image getFocusedIcon() { return focusedIcon; } } | cs |
위 코드를 보면 Treeitem은 Item 에 들어갈 String 값과 이미지를 path 형태로 받습니다. 그런데 여기는 이미지가 두 개밖에 없습니다.
총 4개가 필요한데 말이죠. 그 이유는 Root, Child 일 때를 TreeItem을 상속받은 각각 다른 클래스로 구현하기 위함입니다.
그래서 아래처럼 두 개의 클래스를 만듭니다.
MyDirTreeIte.java
1 2 3 4 5 6 | public class MyDirTreeItem extends Gk2TreeItem { public MyDirTreeItem(String text) { super(text, "normalRoot.png", "focusedRoot.png"); } } | cs |
MyFileTreeItem.java
1 2 3 4 5 6 | public class MyFileTreeItem extends Gk2TreeItem { public MyFileTreeItem(String text) { super(text, "normalChild.png", "focusedChild.png"); } } | cs |
그럼 이제 위에서 처음에 TreeItem 을 구성 할 때도 바꿔줘야 겠죠?
MyTreeView.java 수정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class MyTreeView extends TreeView<String> { private String rootValue = "Root"; private String subRootValue = "Sub Root"; private String DEFAULT_STYLE_CLASS = "gk2-tree-view"; private String CSS_PATH = "/commons/ui/control/treeview/treeview.css"; private String[] rootItem = {"1", "2", "3"}; public MyTreeView() { getStylesheets().add(CSS_PATH); getStyleClass().add(DEFAULT_STYLE_CLASS); setPrefWidth(200); setPrefHeight(200); TreeItem<String> root = new MyRootTreeItem(rootValue); TreeItem<String> subRoot = new MyRootTreeItem(subRootValue); root.setExpanded(true); subRoot.setExpanded(true); for (String itemString : rootItem) { subRoot.getChildren().add(new MyChildTreeItem(itemString)); } root.getChildren().add(subRoot); //TreeCell 인스턴스 생성 this.setCellFactory(treeView -> new MyTreeCell()); this.setRoot(root); // 처음 실행 시 root의 focus를 통해 이상 현상 해결 this.getSelectionModel().select(0); } } | cs |
그럼 이제는 TreeCell 도 바꿔줘야 합니다.
MyTreeCell.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class MyTreeCell extends TreeCell<String> { public MyTreeCell(){ //이벤트 처리.. } @Override protected void updateItem(String item, boolean empty) { super.updateItem(item, empty); if(!empty){ setText(item); MyTreeItem treeItem = (MyTreeItem)getTreeItem(); setGraphic(new ImageView(treeItem.getNormalIcon())); }else{ setText(null); setGraphic(null); } } } | cs |
이제 남은 것은 이벤트를 처리하는 일 밖에 없습니다. 이벤트 처리는 각 Cell 마다 Listener를 달아주는 방식입니다.
위에 주석 부분에 들어갈 코드는 아래와 같습니다.
이벤트 처리
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | hoverProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { if (getItem() != null && !isFocused()) { if (newValue) { MyTreeItem item = (MyTreeItem) getTreeItem(); setGraphic(new ImageView(item.getFocusedIcon())); } else { MyTreeItem item = (MyTreeItem) getTreeItem(); setGraphic(new ImageView(item.getNormalIcon())); } } } }); focusedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { if (getItem() != null ) { if (newValue) { MyTreeItem item = (MyTreeItem) getTreeItem(); setGraphic(new ImageView(item.getFocusedIcon())); }else{ MyTreeItem item = (MyTreeItem) getTreeItem(); setGraphic(new ImageView(item.getNormalIcon())); } } } }); selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { if (newValue) { MyTreeItem item = (MyTreeItem) getTreeItem(); setGraphic(new ImageView(item.getFocusedIcon())); } } }); } | cs |
그래서 결과화면은 아래와 같습니다.
'프로그래밍 > JavaFX' 카테고리의 다른 글
[JavaFX] StringProperty를 이용해 CustomControl fxml에서 Text 수정하 (0) | 2017.02.01 |
---|---|
[JavaFX] FileChooser 를 통해 txt 파일 읽어와 TextArea에 보여주기 (0) | 2017.01.17 |
[JavaFX] Container 내의 요소에 Margin 속성 주는 방법 (0) | 2016.12.27 |
[JavaFX] ComboBox 값 초기화 방법 및 사용법 (1) | 2016.12.26 |
[JavaFX] pop up 창 띄우기 (Popup class 이용) (3) | 2016.12.22 |
- Total
- Today
- Yesterday
- TableView
- 텐트
- windows
- JavaFX 테이블뷰
- JavaFX Window Close
- Java UI
- 인텔리제이
- JavaFX Table View
- 이펙티브자바
- effectivejava
- 이펙티브 자바
- 자전거
- git
- 이펙티브
- 스프링부트
- 일본 여행
- 일본 배낭여행
- JavaFX 종료
- 일본 자전거 여행
- springboot
- 자전거 여행
- effective java
- JavaFX
- 배낭여행
- 배낭 여행
- 자바
- 방통대 과제물
- intelij
- java
- 일본여행
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |