2017-10-16 19 views
0

Mon objectif est d'afficher tous les champs d'une instance d'une classe dans une tableView. La classe a un champ de type enum qui a un champ de type String. L'énumération doit être affichée dans une zone de liste déroulante en tant que nom de zone de chaîne. Bien sûr, il doit également être modifiable.JavaFX Display Enum avec champ String dans Combobox as String (dans TableView)

Maintenant, ce qui ne fonctionne pas: Le champ String de la classe enum est affiché uniquement si vous cliquez sur ComboBox, sinon c'est le nom de la constante enum. En outre, si une autre énumération dans la liste déroulante est sélectionnée, elle ne peut pas être validée pour l'édition. Cliquer sur return ne désélectionne pas le Combobox et la méthode commitEdit n'est pas invoquée. Si une autre colonne est sélectionnée pour l'édition, la modification tentée est annulée. Je me suis efforcé d'essayer de comprendre cela, alors j'ai pensé que peut-être quelqu'un pourrait m'aider ici. Comme la tâche d'origine concerne des classes beaucoup plus grandes dans les logiciels d'entreprise, je l'ai extrait pour poser cette question.

Je sais que je pourrais faire la colonne contenant l'enum de type String et le faire fonctionner avec MyEnum.values ​​() et MyEnum.valueOf() mais cela ne pourrait pas entrer en production en raison de mauvaises performances comme les classes d'origine sont trop gros.

Voici mon code comme exemple, si vous ne comprenez pas les problèmes, essayez d'utiliser la combobox une fois et vous verrez.

classe qui est le type de TableView:

public class MyClass { 

private MyEnum myEnum; 

private String string; 

public MyClass(MyEnum myEnum, String string) { 
    this.myEnum = myEnum; 
    this.string = string; 
} 

public MyEnum getMyEnum() { 
    return myEnum; 
} 

public void setMyEnum(MyEnum myEnum) { 
    this.myEnum = myEnum; 
} 

public String getString() { 
    return string; 
} 

} 

Il est champ ENUM:

public enum MyEnum { 

EnumOne("First Enum"), 
EnumTwo("Second Enum"); 

private String name; 

public String getName() { 
    return name; 
} 

private MyEnum(String name) { 
    this.name = name; 
} 

} 

FX App:

public class NewFXMain extends Application { 

@Override 
public void start(Stage primaryStage) { 
    ObservableList<MyClass> items = FXCollections.observableArrayList(); 
    items.add(new MyClass(MyEnum.EnumOne, "String")); 
    TableView<MyClass> table = new TableView(items); 
    table.setEditable(true); 
    TableColumn<MyClass, MyEnum> myEnumColumn = new TableColumn(); 
    TableColumn<MyClass, String> stringColumn = new TableColumn(); 

    stringColumn.setCellFactory(TextFieldTableCell.forTableColumn()); 
    stringColumn.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getString())); 



    myEnumColumn.setCellFactory((param) -> new MyEnumComboBoxCell()); 
    myEnumColumn.setCellValueFactory(data -> new ReadOnlyObjectWrapper(data.getValue().getMyEnum())); 
    myEnumColumn.setOnEditCommit(
      event -> { 
       event.getRowValue().setMyEnum(event.getNewValue()); 
       System.out.println(event.getRowValue().getMyEnum()); 

      }); 

    table.getColumns().addAll(myEnumColumn, stringColumn); 

    StackPane root = new StackPane(); 
    root.getChildren().add(table); 
    Scene scene = new Scene(root, 300, 250); 
    primaryStage.setScene(scene); 
    primaryStage.show(); 
} 

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

} 

class MyEnumComboBoxCell extends ComboBoxTableCell<MyClass, MyEnum> { 

private ComboBox<MyEnum> box; 

public MyEnumComboBoxCell() { 
    box = new ComboBox<>(FXCollections.observableArrayList(MyEnum.values())); 
    box.setCellFactory(new Callback<ListView<MyEnum>, ListCell<MyEnum>>() { 
     @Override 
     public ListCell<MyEnum> call(ListView<MyEnum> param) { 
      return new ListCell<MyEnum>() { 
       @Override 
       protected void updateItem(MyEnum item, boolean empty) { 
        super.updateItem(item, empty); 
        if (item != null) setText(item.getName()); 
       } 

      }; 
     } 
    }); 
} 

@Override 
public void startEdit() { 
    super.startEdit(); 
    setGraphic(box); 
} 

@Override 
public void commitEdit(MyEnum newValue) { 
    super.commitEdit(newValue); 
    if (newValue != null) { 
     setText(newValue.getName()); 
     getTableView().getSelectionModel().getSelectedItem().setMyEnum(newValue); 
     box.setValue(newValue); 
    } 
} 

@Override 
public void updateItem(MyEnum item, boolean empty) { 
    super.updateItem(item, empty); 
    if (empty) { 
     setGraphic(null); 
    } else { 
     setGraphic(null); 
     setText(item.getName()); 
    } 
} 

} 

Répondre

1

Au lieu de mettre le nom en updateItem utiliser un StringConverter comme :

public class MyEnumConverter extends StringConverter<MyEnum>{ 

    @Override public String toString(MyEnum enumConstant) { 
     return enumConstant.getName(); 
    } 

    @Override public MyEnum fromString(String string) { 
     return MyEnum.valueOf(string); 
    } 
} 

Ensuite, dans le constructeur de la cellule:

this.setConverter(new MyEnumConverter()); 

Edit: Vous ne pouvez pas @Override toutes les méthodes de ComboBoxTableCell, puisque tous d'entre eux travaillent comme vous voulez. D'un autre côté, vous ne devriez pas spécifier un propre ComboBox pour la cellule du tableau, puisqu'il en a un. Il vous suffit d'ajouter un StringConverter et de définir les éléments.

Vous pouvez utiliser comme ceci:

myEnumColumn.setCellFactory((param) -> new ComboBoxTableCell<>(new StringConverter<MyEnum>() { 
      @Override public String toString(MyEnum object) { 
       return object.getName(); 
      } 

      @Override public MyEnum fromString(String string) { 
       return MyEnum.valueOf(string); 
      } 
     }, MyEnum.values())); 

Si vous préférez, vous pouvez créer une catégorie distincte pour la StringConverter comme je l'ai mentionné plus tôt, alors tout simplement:

myEnumColumn.setCellFactory(factory -> new ComboBoxTableCell<>(new MyEnumConverter(), MyEnum.values())); 

Vous pouvez également vous débarrasser du myEnumColumn.setOnEditCommit.

+0

J'ai essayé d'implémenter votre solution mais elle ne fonctionne toujours pas, même si c'est un peu différemment. Avez-vous réellement réécrit mon exemple et cela a-t-il fonctionné? J'ai essayé ça moi-même avant, donc je pense probablement pas? –

+0

J'ai modifié la réponse depuis que j'ai trouvé un autre problème dans votre code. \] – Sunflame

0

Merci beaucoup! En fait, j'ai passé une journée à ce sujet, en partie avec une autre personne, donc c'est vraiment apprécié! :)

Mais: Je dois implémenter setOnEditCommit ou sinon le champ myEnum sauvegardant la tableColonne ne change pas! Avec ça tout fonctionne.Sans, seul ce qui est affiché est changé.

myEnumColumn.setOnEditCommit(
      event -> 
    { 
     event.getRowValue().setMyEnum(event.getNewValue()); 

    }); 
+0

Oui, vous pouvez utiliser cette solution pour définir la valeur du modèle de sauvegarde, mais il existe une façon élégante de créer une classe de modèle tenir l'information d'une rangée, alors tout se passe sans aucune intervention manuelle. – Sunflame