2017-11-21 62 views
0

첫 번째 열에 문자열 값이 저장되고 세 번째 열이 3 상태 체크 박스로 렌더링되는 javafx에서 TreeTableView를 구현하려고합니다. 다음 MCVE를 사용하면 테이블을 만들 수 있지만 확인란의 선택 항목 중 부모 축소/확장 또는 테이블의 크기 조정이 지속되지 않습니다.
MCVE
클래스 A는 부모입니다.
클래스 B는 A를 확장하고 자식입니다.
클래스 C는 두 번째 열 (체크 박스로 렌더링 됨)을 나타냅니다.
클래스 MVCECheckBox는 트리 테이블을 작성하고 표시합니다.Javafx : TreeTableView 내에 3- 상태 체크 박스를 구현하는 방법

A.java

package mcve.checkbox; 

import java.util.List; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

/** 
* 
* @author returncode13 
*/ 
public class A { 
    StringProperty name=new SimpleStringProperty(); 
    C check=new C(); 
    List<A> children; 

    public StringProperty getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name.set(name); 
    } 

    public C getCheck() { 
     return check; 
    } 

    public void setCheck(C check) { 
     this.check = check; 
    } 

    public StringProperty nameProperty(){ 
     return name; 
    } 

    public List<A> getChildren() { 
     return children; 
    } 

    public void setChildren(List<A> children) { 
     this.children = children; 
    } 


    } 

B.java

package mcve.checkbox; 

import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

/** 
* 
* @author returncode13 
*/ 
public class B extends A{ 
    StringProperty name=new SimpleStringProperty(); 
    C Check=new C(); 

    @Override 
    public StringProperty getName() { 
     return name; 
    } 

    @Override 
    public void setName(String name) { 
     this.name.set(name); 
    } 

    @Override 
    public C getCheck() { 
     return Check; 
    } 

    @Override 
    public void setCheck(C Check) { 
     this.Check = Check; 
    } 

    @Override 
    public StringProperty nameProperty(){ 
     return name; 
    } 

} 

C.java

package mcve.checkbox; 

import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.SimpleBooleanProperty; 

/** 
* 
* @author returncode13 
*/ 
public class C { 
    BooleanProperty checkUncheck=new SimpleBooleanProperty(); 
    BooleanProperty indeterminate=new SimpleBooleanProperty(); 

    public BooleanProperty getCheckUncheck() { 
     return checkUncheck; 
    } 

    public void setCheckUncheck(BooleanProperty checkUncheck) { 
     this.checkUncheck = checkUncheck; 
    } 

    public BooleanProperty getIndeterminate() { 
     return indeterminate; 
    } 

    public void setIndeterminate(BooleanProperty indeterminate) { 
     this.indeterminate = indeterminate; 
    } 

    public BooleanProperty checkUncheckProperty(){ 
     return checkUncheck; 
    } 

    public BooleanProperty indeterminateProperty(){ 
     return indeterminate; 
    } 
} 

MCVECheckBox.java

package mcve.checkbox; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.beans.value.ObservableValue; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.CheckBox; 
import javafx.scene.control.TreeItem; 
import javafx.scene.control.TreeTableCell; 
import javafx.scene.control.TreeTableColumn; 
import javafx.scene.control.TreeTableView; 
import javafx.scene.control.cell.TreeItemPropertyValueFactory; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 
import javafx.util.Callback; 

/** 
* 
* @author returncode13 
*/ 
public class MCVECheckBox extends Application { 
    A selectedItem; 
    private TreeTableView<A> treetable=new TreeTableView<>(); 
    @Override 
    public void start(Stage primaryStage) { 
     //setting up parents (A) and children (B) 
     A a1=new A();      
     a1.setName("A1"); 

     List<A> A1Children=new ArrayList(); 
     B b11=new B(); 
     b11.setName("B11"); 
     B b12=new B(); 
     b12.setName("B12"); 
     A1Children.add(b11); 
     A1Children.add(b12); 
     a1.setChildren(A1Children); 


     A a2=new A(); 
     a2.setName("A2"); 

     List<A> A2Children=new ArrayList(); 
     B b21=new B(); 
     b21.setName("B21"); 
     B b22=new B(); 
     b22.setName("B22"); 
     A2Children.add(b21); 
     A2Children.add(b22); 
     a2.setChildren(A2Children); 



     //tree columns . first one holds strings 
     TreeTableColumn<A,String> name=new TreeTableColumn<>("Name"); 
     name.setCellValueFactory(new TreeItemPropertyValueFactory<>("name")); 


     //2nd tree columns. rendered as checkboxes. boolean values 
     TreeTableColumn<A,Boolean> checks=new TreeTableColumn<>("Checks"); 
     checks.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<A, Boolean>, ObservableValue<Boolean>>() { 
      @Override 
      public ObservableValue<Boolean> call(TreeTableColumn.CellDataFeatures<A, Boolean> param) { 
       A a=param.getValue().getValue(); 
       SimpleBooleanProperty checkUncheck=new SimpleBooleanProperty(); 
       SimpleBooleanProperty indeterminate=new SimpleBooleanProperty(); 

       checkUncheck=(SimpleBooleanProperty) a.getCheck().getCheckUncheck(); 
       indeterminate=(SimpleBooleanProperty) a.getCheck().getIndeterminate(); 

       //to do: set parents status based on children status. 


       if(indeterminate.get()){ 
        return indeterminate; 
       }else{ 
        return checkUncheck; 
       } 

      } 
     }); 


     checks.setCellFactory(new Callback<TreeTableColumn<A, Boolean>, TreeTableCell<A, Boolean>>() { 
      @Override 
      public TreeTableCell<A, Boolean> call(TreeTableColumn<A, Boolean> param) { 
       return new CheckBoxCell(); 
      } 
     }); 


     //building the tree; 
     TreeItem<A> a1item=new TreeItem<>(a1); 
     TreeItem<A> b11item=new TreeItem<>(b11); 
     TreeItem<A> b12item=new TreeItem<>(b12); 
     a1item.getChildren().add(b11item); 
     a1item.getChildren().add(b12item); 

     TreeItem<A> a2item=new TreeItem<>(a2); 
     TreeItem<A> b21item=new TreeItem<>(b21); 
     TreeItem<A> b22item=new TreeItem<>(b22); 
     a2item.getChildren().add(b21item); 
     a2item.getChildren().add(b22item); 




     TreeItem<A> root=new TreeItem<>(); 
     root.getChildren().add(a1item); 
     root.getChildren().add(a2item); 

     treetable.getColumns().addAll(name,checks); 

     treetable.setRoot(root); 
     treetable.setShowRoot(false); 
     treetable.setEditable(true); 


     // StackPane rootSp = new StackPane(); 
     Scene scene = new Scene(treetable, 300, 250); 

     primaryStage.setTitle("Hello World!"); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     launch(args); 
    } 


    //to render checkboxes in treetable 


    private class CheckBoxCell extends TreeTableCell<A, Boolean> { 


     CheckBox checkbox; 

     public CheckBoxCell() { 
      checkbox=new CheckBox(); 
      checkbox.setAllowIndeterminate(true); 

      checkbox.selectedProperty().addListener((obs,wasSelected,isNowSelected) -> { 
      if(isNowSelected){ 
       selectedItem=getTreeTableRow().getItem(); 
      } 
      }); 

     } 


     @Override 
     public void updateItem(Boolean b,boolean empty){ 
      super.updateItem(b, empty); 

      if(empty){ 
       setGraphic(null); 
      }else{ 
       checkbox.setSelected(b); 
       setGraphic(checkbox); 
      } 
     } 
    } 

} 

이전에 CheckTreeTableCell을 사용하여 두 번째 열의 셀 팩터를 설정했지만 CheckTreeTableCell이 3- 상태 (check, uncheck, indeterminate) 확인란을 지원하지 않는다는 것을 알았습니다. 그 후 위 코드를 구현하려고 시도했습니다. 세 가지 상태 체크 박스를 가져올 수는 있지만 상태를 유지할 수는 없습니다. 부모가 축소/확장 될 때마다 자녀에 대한 검사는 선택 취소됩니다.
수정 프로그램에 대한 도움을 주셔서 감사합니다.

답변

0

게시 된 MCVE에 다음과 같이 수정 된 3 상태 확인란을 구현할 수있게되었습니다. 이제는 완전한 작동 예제입니다.

A.java (부모 클래스)

package com.mycompany.yetanothercheckbox; 

import java.util.Iterator; 
import java.util.List; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

/** 
* 
* @author returncode13 
*/ 
public class A { 
    private StringProperty name=new SimpleStringProperty(); 
    C check=new C(); 
    List<A> children; 
    final boolean isLeaf=false; 
    final boolean isParent=true; 
    public boolean updateParent=false; 
    public boolean updateChildren=false; 
    public boolean isLeaf() { 
     return isLeaf; 
    } 

    public boolean isParent() { 
     return isParent; 
    } 
    public StringProperty getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name.set(name); 
    } 

    public C getCheck() { 
     return check; 
    } 

    public void setCheck(C check) { 
     this.check = check; 
     for (Iterator<A> iterator = children.iterator(); iterator.hasNext();) { 
      A next = iterator.next(); 
      next.setCheck(check); 

     } 
    } 

    public StringProperty nameProperty(){ 
     return name; 
    } 

    public List<A> getChildren() { 
     return children; 
    } 

    public void setChildren(List<A> children) { 
     this.children = children; 
    } 

    public A getParent() { 
     return this; 
    } 
} 

B.java (자식 클래스)

package com.mycompany.yetanothercheckbox; 

import java.util.List; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

/** 
* 
* @author returncode13 
*/ 
public class B extends A{ 
    private StringProperty name=new SimpleStringProperty(); 
    C Check=new C(); 
    final boolean isLeaf=true; 
    final boolean isParent=false; 
    public boolean updateParent=false; 
    public boolean updateChildren=false; 
    A parent; 

    public A getParent() { 
     return parent; 
    } 

    public void setParent(A parent) { 
     this.parent = parent; 
    } 


    @Override 
    public boolean isLeaf() { 
     return isLeaf; 
    } 

    @Override 
    public boolean isParent() { 
     return isParent; 
    } 



    @Override 
    public StringProperty getName() { 
     return name; 
    } 

    @Override 
    public void setName(String name) { 
     this.name.set(name); 
    } 

    @Override 
    public C getCheck() { 
     return Check; 
    } 

    @Override 
    public void setCheck(C Check) { 
     this.Check = Check; 
    } 

    @Override 
    public StringProperty nameProperty(){ 
     return name; 
    } 

    @Override 
     public List<A> getChildren() { 
     return parent.getChildren(); 
    } 

} 

C.java (점검 보류 상태)

package com.mycompany.yetanothercheckbox; 

import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.SimpleBooleanProperty; 

/** 
* 
* @author returncode13 
*/ 
public class C { 
    BooleanProperty checkUncheck=new SimpleBooleanProperty(); 
    BooleanProperty indeterminate=new SimpleBooleanProperty(); 

    public BooleanProperty getCheckUncheck() { 
     return checkUncheck; 
    } 

    public void setCheckUncheck(BooleanProperty checkUncheck) { 
     this.checkUncheck = checkUncheck; 
    } 

    public BooleanProperty getIndeterminate() { 
     return indeterminate; 
    } 

    public void setIndeterminate(BooleanProperty indeterminate) { 
     this.indeterminate = indeterminate; 
    } 

    public BooleanProperty checkUncheckProperty(){ 
     return checkUncheck; 
    } 

    public BooleanProperty indeterminateProperty(){ 
     return indeterminate; 
    } 
} 

ThreeStateCheckBoxTreeTableCell.java (나무 테이블에 대한 3 단계 확인란)

package com.mycompany.yetanothercheckbox; 

import java.util.List; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.event.EventHandler; 
import javafx.scene.control.CheckBox; 
import javafx.scene.control.TreeTableCell; 
import javafx.scene.control.TreeTableColumn; 
import javafx.scene.input.MouseEvent; 

/** 
* 
* @author returncode13 
*/ 
    //to render checkboxes in treetable 


    public class ThreeStateCheckBoxTreeTableCell extends TreeTableCell<A, Boolean> { 


     A selectedItem; 
     CheckBox checkbox; 
     TreeTableColumn<A,Boolean> param; 
     /*private static boolean updateParent=false; 
     private static boolean updateChildren=false;*/ 


     public ThreeStateCheckBoxTreeTableCell(TreeTableColumn<A,Boolean> param) { 
      checkbox=new CheckBox(); 
      this.param=param; 
      checkbox.setAllowIndeterminate(true); 

      checkbox.selectedProperty().addListener((obs,wasSelected,isNowSelected) -> { 
       int sel=getTreeTableRow().getIndex(); 
       selectedItem=this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue(); 
       selectedItem.getCheck().getCheckUncheck().set(isNowSelected); 
       selectedItem.getCheck().getIndeterminate().set(false); 

       //ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh(); 


      }); 


      checkbox.indeterminateProperty().addListener((obx,ol,newV)->{ 

       int sel=getTreeTableRow().getIndex(); 
       selectedItem=this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue(); 
       selectedItem.getCheck().getIndeterminate().set(newV); 

      //ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh(); 
      }); 



      checkbox.setOnMouseClicked(new EventHandler<MouseEvent>(){ 
       @Override 
       public void handle(MouseEvent event) { 
        int sel=getTreeTableRow().getIndex(); 
        selectedItem=ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue(); 
        if(selectedItem.isParent()){ 

         selectedItem.updateChildren=true; 
         for(A child:selectedItem.getChildren()){ 
          child.updateParent=false; 
         } 


         updateDownwards(); 
        } 
        if(selectedItem.isLeaf()){ 

         selectedItem.getParent().updateChildren=false; 
         selectedItem.updateParent=true; 

         updateUpWards(); 
        } 

        ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh(); 
       } 

      }); 
     } 


     @Override 
     public void updateItem(Boolean b,boolean empty){ 
      super.updateItem(b, empty); 

      if(empty){ 
       setGraphic(null); 
      }else{ 


       if(b==null){ 
        checkbox.setIndeterminate(true); 

       } 
       else{ 
        checkbox.setIndeterminate(false); 
        checkbox.setSelected(b); 

       } 
       setGraphic(checkbox); 

      } 

      ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh(); 
     } 

     private void updateUpWards(){ 

      if(selectedItem.updateParent){ 


      List<A> children=selectedItem.getChildren(); 
      int indeterminateCount=0; 
      int selectedCount=0; 

       A parent=selectedItem.getParent(); 
       for(A child:children){ 
        indeterminateCount+=child.getCheck().getIndeterminate().get()?1:0; 
        selectedCount+=child.getCheck().getCheckUncheck().get()?1:0; 

       } 

       if(indeterminateCount>0) { 

        parent.getCheck().getIndeterminate().set(true); 
       } 
       else if(indeterminateCount==0 && selectedCount==children.size()){ 
        parent.getCheck().getIndeterminate().set(false); 
        parent.getCheck().getCheckUncheck().set(true); 
       }else{ 

        parent.getCheck().getIndeterminate().set(false); 
        parent.getCheck().getCheckUncheck().set(false); 
       } 

      } 
      ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh(); 


     } 


     private void updateDownwards(){ 
      List<A> children=selectedItem.getChildren(); 

      if(selectedItem.isParent() && selectedItem.updateChildren){ 

       for(A child:children){ 
        child.getCheck().getCheckUncheck().set(selectedItem.getCheck().getCheckUncheck().get()); 

        child.getCheck().getIndeterminate().set(selectedItem.getCheck().getIndeterminate().get()); 
       } 


      } 
      ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh(); 

     } 
    } 

MainApp.java (a POC로서 출원)

package com.mycompany.yetanothercheckbox; 

import java.util.ArrayList; 
import java.util.Iterator; 
import java.util.List; 
import javafx.application.Application; 
import static javafx.application.Application.launch; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.CheckBox; 
import javafx.scene.control.TreeItem; 
import javafx.scene.control.TreeTableCell; 
import javafx.scene.control.TreeTableColumn; 
import javafx.scene.control.TreeTableView; 
import javafx.scene.control.cell.TreeItemPropertyValueFactory; 
import javafx.stage.Stage; 
import javafx.util.Callback; 


public class MainApp extends Application { 

    private TreeTableView<A> treetable=new TreeTableView<>(); 
    @Override 
    public void start(Stage primaryStage) { 
     //setting up parents (A) and children (B) 
     A a1=new A();      
     a1.setName("A1"); 

     List<A> A1Children=new ArrayList(); 
     B b11=new B(); 
     b11.setName("B11"); 
     B b12=new B(); 
     b12.setName("B12"); 
     b11.setParent(a1); 
     b12.setParent(a1); 
     A1Children.add(b11); 
     A1Children.add(b12); 
     a1.setChildren(A1Children); 


     A a2=new A(); 
     a2.setName("A2"); 

     List<A> A2Children=new ArrayList(); 
     B b21=new B(); 
     b21.setName("B21"); 
     B b22=new B(); 
     b22.setName("B22"); 
     b21.setParent(a2); 
     b22.setParent(a2); 
     A2Children.add(b21); 
     A2Children.add(b22); 
     a2.setChildren(A2Children); 



     //tree columns . first one holds strings 
     TreeTableColumn<A,String> name=new TreeTableColumn<>("Name"); 
     name.setCellValueFactory(new TreeItemPropertyValueFactory<>("name")); 


     //2nd tree columns. rendered as checkboxes. boolean values 
     TreeTableColumn<A,Boolean> checks=new TreeTableColumn<>("Checks"); 
     checks.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<A, Boolean>, ObservableValue<Boolean>>() { 
      @Override 
      public ObservableValue<Boolean> call(TreeTableColumn.CellDataFeatures<A, Boolean> param) { 
       A a=param.getValue().getValue(); 
       SimpleBooleanProperty checkUncheck=new SimpleBooleanProperty(); 
       SimpleBooleanProperty indeterminate=new SimpleBooleanProperty(); 

       checkUncheck.bindBidirectional(a.getCheck().getCheckUncheck()); 
       indeterminate.bindBidirectional(a.getCheck().getIndeterminate()); 


       checkUncheck.addListener(new ChangeListener<Boolean>(){ 
        @Override 
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 

         a.getCheck().indeterminateProperty().set(false); 
         a.getCheck().checkUncheckProperty().set(newValue); 
        } 

       }); 
       indeterminate.addListener(new ChangeListener<Boolean>(){ 
        @Override 
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 

          a.getCheck().indeterminateProperty().set(newValue); 

        } 
       }); 
       if(indeterminate.get()){ 

       return null; 
       }else{ 

        return checkUncheck; 
       } 

      } 
     }); 


     checks.setCellFactory(new Callback<TreeTableColumn<A, Boolean>, TreeTableCell<A, Boolean>>() { 
      @Override 
      public TreeTableCell<A, Boolean> call(TreeTableColumn<A, Boolean> param) { 
       return new ThreeStateCheckBoxTreeTableCell(param); 
      } 
     }); 


     //building the tree; 
     TreeItem<A> a1item=new TreeItem<>(a1); 
     TreeItem<A> b11item=new TreeItem<>(b11); 
     TreeItem<A> b12item=new TreeItem<>(b12); 
     a1item.getChildren().add(b11item); 
     a1item.getChildren().add(b12item); 

     TreeItem<A> a2item=new TreeItem<>(a2); 
     TreeItem<A> b21item=new TreeItem<>(b21); 
     TreeItem<A> b22item=new TreeItem<>(b22); 
     a2item.getChildren().add(b21item); 
     a2item.getChildren().add(b22item); 




     TreeItem<A> root=new TreeItem<>(); 
     root.getChildren().add(a1item); 
     root.getChildren().add(a2item); 

     treetable.getColumns().addAll(name,checks); 

     treetable.setRoot(root); 
     treetable.setShowRoot(false); 
     treetable.setEditable(true); 


     // StackPane rootSp = new StackPane(); 
     Scene scene = new Scene(treetable, 300, 250); 

     primaryStage.setTitle("CheckBoxTreeTable Example"); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     launch(args); 
    } 




}