키는 셀 공장에서 Task
(이 예제에서는 Task
대신 Service
을 여러 번 실행할 수 있기 때문에 사용됨) 상태를 관찰하는 것입니다. 이를 수행하려면 TreeItem
의 데이터 유형이 태스크/서비스의 현재 상태를 나타내는 관찰 가능한 특성을 가지고 있어야합니다. 가능하다면 가장 쉬운 방법은 TreeItem
의 데이터 형식을 Task
으로 만드는 것입니다 (개념적으로 TreeView
은 Task
을 표시 함).
주어진 셀이 나타내는 항목 (즉, 작업)이 변경 될 수 있으므로 약간 미묘합니다. 이 예제에서 셀의 item
속성을 관찰하고, 셀이 더 이상 나타내지 않는 항목에서 작업의 상태를 관찰하는 수신기를 제거하고 현재 표시된 항목에 수신기를 추가합니다. 당신이 EasyBind 프레임 워크 (그리고 필요한 자바 8)를 사용하는 경우, 당신은
EasyBind.select(cell.itemProperty())
.selectObject(Service::stateProperty)
.addListener((ov, oldState, newState) -> updateCell(cell));
전체 예 (I 자바 8에서 컴파일하지만, 자바 FX 2.2을 사용하여 같은 일을하고, 조금이 정리, 그래서 약간의 수
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
final BorderPane root = new BorderPane();
final TreeView<SelectableService> tree = new TreeView<>();
final TreeItem<SelectableService> treeRoot = new TreeItem<>(new SelectableService("Parent"));
for (int i=1; i<=10; i++) {
treeRoot.getChildren().add(new TreeItem<>(new SelectableService("Task "+i)));
}
tree.setRoot(treeRoot);
final Button startButton = new Button("Start selected tasks");
startButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
for (SelectableService service : findSelectedTasks(treeRoot)) {
service.restart();
}
}
});
final HBox controls = new HBox(5);
controls.getChildren().add(startButton);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
root.setCenter(tree);
root.setBottom(controls);
tree.setCellFactory(new Callback<TreeView<SelectableService>, TreeCell<SelectableService>>() {
@Override
public TreeCell<SelectableService> call(TreeView<SelectableService> param) {
return createCell();
}
});
Scene scene = new Scene(root, 400, 600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
private CheckBoxTreeCell<SelectableService> createCell() {
// CheckBoxTreeCell whose check box state is mapped to the selected property of the task:
final CheckBoxTreeCell<SelectableService> cell = new CheckBoxTreeCell<SelectableService>(new Callback<TreeItem<SelectableService>, ObservableValue<Boolean>>() {
@Override
public ObservableValue<Boolean> call(TreeItem<SelectableService> treeItem) {
SelectableService task = treeItem.getValue();
if (task != null) {
return task.selectedProperty();
} else {
return null ;
}
}
});
final ChangeListener<State> taskStateListener = new ChangeListener<State>() {
@Override
public void changed(
ObservableValue<? extends State> observable,
State oldValue, State newValue) {
updateCell(cell);
}
};
cell.itemProperty().addListener(new ChangeListener<SelectableService>() {
@Override
public void changed(
ObservableValue<? extends SelectableService> observable,
SelectableService oldTask, SelectableService newTask) {
if (oldTask != null) {
oldTask.stateProperty().removeListener(taskStateListener);
}
if (newTask != null) {
newTask.stateProperty().addListener(taskStateListener);
}
updateCell(cell);
}
});
cell.setConverter(new StringConverter<TreeItem<SelectableService>>() {
@Override
public String toString(TreeItem<SelectableService> treeItem) {
SelectableService task = treeItem.getValue();
if (task == null) {
return null ;
} else {
return task.getName();
}
}
@Override
public TreeItem<SelectableService> fromString(String string) {
// Not supported
throw new UnsupportedOperationException("Uneditable tree cell does not create SelectableTasks");
}
});
return cell;
}
private void updateCell(CheckBoxTreeCell<SelectableService> cell) {
cell.getStyleClass().removeAll(Arrays.asList("running", "finished", "failed"));
SelectableService task = cell.getItem();
if (task != null) {
State state = task.getState();
// Update style class:
if (state == State.RUNNING) {
cell.getStyleClass().add("running");
} else if (state == State.SUCCEEDED) {
cell.getStyleClass().add("finished");
} else if (state == State.FAILED){
cell.getStyleClass().add("failed");
}
}
}
private Set<SelectableService> findSelectedTasks(TreeItem<SelectableService> treeItem) {
Set<SelectableService> selectedTasks = new HashSet<>();
addTaskAndChildTasksIfSelected(selectedTasks, treeItem) ;
return selectedTasks ;
}
private void addTaskAndChildTasksIfSelected(Set<SelectableService> selectedTasks, TreeItem<SelectableService> treeItem) {
SelectableService task = treeItem.getValue();
if (task != null && task.isSelected()) {
selectedTasks.add(task);
}
for (TreeItem<SelectableService> child : treeItem.getChildren()) {
addTaskAndChildTasksIfSelected(selectedTasks, child);
}
}
public static class SelectableService extends Service<Void> {
private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected", false);
public final BooleanProperty selectedProperty() {
return this.selected;
}
public final boolean isSelected() {
return this.selectedProperty().get();
}
public final void setSelected(final boolean selected) {
this.selectedProperty().set(selected);
}
private final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper(this, "name");
private final void setName(String name) {
this.name.set(name);
}
public final String getName() {
return name.get() ;
}
public final ReadOnlyStringProperty nameProperty() {
return name.getReadOnlyProperty();
}
public SelectableService(String name) {
setExecutor(Executors.newCachedThreadPool(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
}
}));
setName(name);
}
@Override
public Task<Void> createTask() {
return new Task<Void>() {
@Override
public Void call() throws Exception {
// just a mock task: pauses for a random time, then throws an exception with
// probability 0.25
Random rng = new Random();
Thread.sleep(2000 + rng.nextInt(2000));
if (rng.nextDouble() < 0.25) {
throw new Exception("Task failed");
}
return null ;
}
};
}
}
public static void main(String[] args) {
launch(args);
}
}
application.css는
.finished {
-fx-background: green ;
}
.failed {
-fx-background: red ;
}
.running {
-fx-background: yellow ;
}
이 그렇고, 아주 상당히 청소기 자바 8 단순히,하지만 당신은 JAV을 게시 이후 : 자바 8 기능)에서 몰래했을 수 있습니다 aFX 2.2 스타일의 코드, 당신이 여전히 이전 버전을 사용하고 있다고 가정했습니다. Java 8에서는 css 스타일에 pseudoclasses를 사용할 수도 있습니다.이 스타일은 좀 더 좋지만 (일반적으로 더 나은 성능을 제공하지만 여기서 논점이 있습니다.)
JavaFX에서 'SwingWorker'를 사용할 수 없습니다. JavaFX는 Swing과 다른 UI 스레드에서 실행됩니다. [Task'에 대한 Javadocs] (http://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html) –
@James_D : 고마워, 그것에 대해 궁금해했다. 너무. 게시물의 문제를 해결하는 데 도움이 될까요? 바로 그 실에서 일하는 것 말고 다른 것. –
예, '태스크'는 FX 어플리케이션 스레드에서 다시 업데이트를 수행하는 메커니즘을 내장하고 있습니다. javadocs에 많은 예제가있다. –