2014-09-09 4 views
0

첫 번째 javafx (2.2) 응용 프로그램을 빌드하고 있습니다. 사용자는 트리 뷰에서 체크 박스를 선택하여 실행할 여러 작업을 선택합니다.업데이트 CheckBoxTreeCell 스타일 외부 프로세스

작업 완료 후 관련 TreeCell의 스타일을 변경하는 방법을 파악하려고합니다.

public class WorkbenchSscce extends Application { 

    public static void main(String...args) { 

     launch(args); 
    } 

    @Override 
    public void start(Stage stage) throws Exception { 

     final CheckBoxTreeItem<String> rootNode = new CheckBoxTreeItem<>("parent"); 
     final CheckBoxTreeItem<String> taskOne = new CheckBoxTreeItem<>("task one"); 
     final CheckBoxTreeItem<String> taskTwo = new CheckBoxTreeItem<>("task two"); 
     rootNode.getChildren().addAll(taskOne, taskTwo); 

     TreeView<String> treeView = new TreeView<>(rootNode); 
     treeView.setEditable(true); 
     treeView.setCellFactory(CheckBoxTreeCell.<String>forTreeView()); 
     treeView.setShowRoot(false); 

     Button executeButton = new Button("Execute"); 
     executeButton.setOnMouseClicked(new EventHandler<MouseEvent>() { 

      @Override 
      public void handle(MouseEvent mouseEvent) { 

       if (taskOne.isSelected()) { 
        executeTask(1); 
        /** 
        * ????? 
        * give the TreeCell for taskOne a green background, to indicate it is complete 
        * ????? 
        */ 
       } 
       if (taskTwo.isSelected()) { 
        executeTask(2); 
        /** 
        * ????? 
        * give the TreeCell for taskTwo a green background, to indicate it is complete 
        * ????? 
        */ 
       } 
      } 
     }); 
     VBox box = new VBox(); 
     box.getChildren().addAll(treeView, executeButton); 
     Scene scene = new Scene(box); 
     stage.setScene(scene); 
     stage.show(); 
    } 

    public void executeTask(int input) { 

     // do something 
    } 
} 

작성시 CheckBoxTreeCells의 스타일을 지정하는 방법을 알 수 있습니다.

사용자 (EventListeners 사용) 이벤트가 발생하면 스타일을 변경하는 방법을 참조하십시오.

그러나 이벤트 소스가 응용 프로그램 내부에있을 때 트리 셀을 스타일하는 방법을 볼 수 없습니다. 위의 MouseEvent 핸들러의 주석을 참조하십시오.

+0

JavaFX에서 'SwingWorker'를 사용할 수 없습니다. JavaFX는 Swing과 다른 UI 스레드에서 실행됩니다. [Task'에 대한 Javadocs] (http://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html) –

+0

@James_D : 고마워, 그것에 대해 궁금해했다. 너무. 게시물의 문제를 해결하는 데 도움이 될까요? 바로 그 실에서 일하는 것 말고 다른 것. –

+0

예, '태스크'는 FX 어플리케이션 스레드에서 다시 업데이트를 수행하는 메커니즘을 내장하고 있습니다. javadocs에 많은 예제가있다. –

답변

0

키는 셀 공장에서 Task (이 예제에서는 Task 대신 Service을 여러 번 실행할 수 있기 때문에 사용됨) 상태를 관찰하는 것입니다. 이를 수행하려면 TreeItem의 데이터 유형이 태스크/서비스의 현재 상태를 나타내는 관찰 가능한 특성을 가지고 있어야합니다. 가능하다면 가장 쉬운 방법은 TreeItem의 데이터 형식을 Task으로 만드는 것입니다 (개념적으로 TreeViewTask을 표시 함).

주어진 셀이 나타내는 항목 (즉, 작업)이 변경 될 수 있으므로 약간 미묘합니다. 이 예제에서 셀의 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를 사용할 수도 있습니다.이 스타일은 좀 더 좋지만 (일반적으로 더 나은 성능을 제공하지만 여기서 논점이 있습니다.)