2016-08-31 3 views
1

Je souhaite redimensionner certaines colonnes TableView, car une méthode pour y parvenir est manquante dans javafx. J'ai réussi à trouver une solution exposée dans MainApp.GUIUtil.fitColumns(TableView tableView).
Mon problème est le suivant: cette solution fonctionne correctement lorsqu'elle est appelée par une action de l'utilisateur, mais je ne trouve pas le moyen d'exécuter cette méthode au démarrage avant toute intervention de l'utilisateur.
Je veux présenter la table avec la colonne adaptée comme soos que la table est montrée.
Comme vous pouvez le voir, j'intercepte le Exception qui me cause des maux de tête (PersonTableController.setMainApp ligne 32), j'imprime le stacktrace et laisse le programme continuer à prouver que la méthode fit marche après que le contrôle soit donné à l'utilisateur.
Comment puis-je ajuster les tailles de colonnes par code dès que la vue de table est affichée?Les colonnes JavaFX TableView correspondent au contenu

Exception in Application start method 
java.lang.reflect.InvocationTargetException 
    ... 
Caused by: java.lang.RuntimeException: Exception in Application start method 
    ... 
Caused by: java.lang.NullPointerException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at columnstofit.MainApp$GUIUtil.fitColumns(MainApp.java:120) 
    at columnstofit.PersonTableController.setMainApp(PersonTableController.java:35) 
    at columnstofit.MainApp.showPersonTable(MainApp.java:79) 
    at columnstofit.MainApp.start(MainApp.java:65) 
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863) 
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326) 
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294) 
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) 
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191) 
    ... 1 more 
Exception running application columnstofit.MainApp 

Voici mon code:

package columnstofit; 

import com.sun.javafx.scene.control.skin.TableViewSkin; 
import java.awt.AWTException; 
import java.io.IOException; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import javafx.application.Application; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Scene; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.layout.AnchorPane; 
import javafx.stage.Stage; 

public class MainApp extends Application { 

    private  PersonTableController controller; 
    public static Stage     primaryStage; 
        AnchorPane   personTable; 

    private ObservableList<Person> personData = FXCollections.observableArrayList(); 

    /** 
    * Constructor 
    */ 
    public MainApp() { 
     // i am entering this name just to force the resizing of the column 
     personData.add(new Person("Hansgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg", "Muster")); 
     personData.add(new Person("Ruth", "Mueller")); 
     personData.add(new Person("Heinz", "Kurz")); 
     personData.add(new Person("Cornelia", "Meier")); 
     personData.add(new Person("Werner", "Meyer")); 
     personData.add(new Person("Lydia", "Kunz")); 
     personData.add(new Person("Anna", "Best")); 
     personData.add(new Person("Stefan", "Meier")); 
    } 

    /** 
    * Returns the data as an observable list of Persons. 
    * @return 
    */ 
    public ObservableList<Person> getPersonData() { 
     return personData; 
    } 

    @Override 
    public void start(Stage primaryStage) throws AWTException { 

     this.primaryStage = primaryStage; 
     this.primaryStage.setTitle("Names Table"); 

     showPersonTable(); 
    } 

    public void showPersonTable() throws AWTException { 

     try 
     { 
      // Load root layout from fxml file. 
      FXMLLoader loader = new FXMLLoader(); 
      loader.setLocation(MainApp.class.getResource("PersonTable.fxml")); 
      personTable = (AnchorPane) loader.load(); 

      // Give the controller access to the main app. 
      controller = loader.getController(); 
      controller.setMainApp(this); 

      Scene scene = new Scene(personTable); 
      primaryStage.setScene(scene); 
      primaryStage.show(); 
     } 
     catch (IOException e) { e.printStackTrace(); } 
    } 

    /** 
    * Returns the main stage. 
    * @return 
    */ 
    public Stage getPrimaryStage() { 
     return primaryStage; 
    } 

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


    public static class GUIUtil { 
     private static Method columnToFitMethod; 

     static 
     { 
      try 
      { 
       columnToFitMethod = TableViewSkin.class.getDeclaredMethod("resizeColumnToFitContent", TableColumn.class, int.class); 
       columnToFitMethod.setAccessible(true); 
      } 
      catch (NoSuchMethodException e) {e.printStackTrace();} 
     } 

     public static void fitColumns(TableView tableView) { 
      for (Object column : tableView.getColumns()) 
      { 
       try { columnToFitMethod.invoke(tableView.getSkin(), column, -1); } 
       catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } 
      } 
     } 
    } 

    public class Person { 
     private final StringProperty firstName; 
     private final StringProperty lastName; 

     public Person() { 
      this(null, null); 
     } 

     public Person(String firstName, String lastName) { 
      this.firstName = new SimpleStringProperty(firstName); 
      this.lastName = new SimpleStringProperty(lastName); 
     } 

     public String getFirstName() { 
      return firstName.get(); 
     } 

     public void setFirstName(String firstName) { 
      this.firstName.set(firstName); 
     } 

     public StringProperty firstNameProperty() { 
      return firstName; 
     } 

     public String getLastName() { 
      return lastName.get(); 
     } 

     public void setLastName(String lastName) { 
      this.lastName.set(lastName); 
     } 

     public StringProperty lastNameProperty() { 
      return lastName; 
     } 
    } 
} 

Et voici le fichier FXML:

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.control.TableColumn?> 
<?import javafx.scene.control.TableView?> 
<?import javafx.scene.layout.AnchorPane?> 

<AnchorPane id="AnchorPane" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="columnstofit.PersonTableController"> 
    <TableView fx:id="tableView" layoutX="-39.0" layoutY="39.0" onKeyPressed="#fitColumns" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> 
        <columns> 
         <TableColumn fx:id="firstNameColumn" editable="false" minWidth="-1.0" prefWidth="-1.0" text="First Name" /> 
         <TableColumn fx:id="lastNameColumn" editable="false" minWidth="-1.0" prefWidth="-1.0" text="Last Name" /> 
        </columns> 
        <columnResizePolicy> 
         <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> 
        </columnResizePolicy> 
        </TableView> 
</AnchorPane> 

Avec son contrôleur respectif:

package columnstofit; 

import java.awt.AWTException; 
import javafx.fxml.FXML; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 

    /** 
    * FXML Controller class 
    */ 
    public class PersonTableController { 

     // Reference to the main application. 
     private MainApp mainApp; 

     @FXML 
     private TableColumn<MainApp.Person, String> firstNameColumn; 
     @FXML 
     private TableColumn<MainApp.Person, String> lastNameColumn; 
     @FXML 
     private TableView       tableView; 

     public PersonTableController() { 
     } 

     public void setMainApp(MainApp mainApp) throws AWTException { 
      this.mainApp = mainApp; 
      // Add observable list data to the table 
      tableView.setItems(mainApp.getPersonData()); 
      try 
      { 
       fitColumns(); 
      } 
      catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 

     @FXML 
     private void initialize() throws AWTException { 
      // Initialize the person table with the two columns. 
      firstNameColumn.setCellValueFactory(
        cellData -> cellData.getValue().firstNameProperty()); 
      lastNameColumn.setCellValueFactory(
        cellData -> cellData.getValue().lastNameProperty()); 
     } 

     @FXML 
     private void fitColumns() { 
      MainApp.GUIUtil.fitColumns(tableView); 
     } 
    } 

Mon idée était d'appeler cette méthode à la fin du rendu, donc j'ai essayé wi cette solution: Post render event in JavaFX, mais rien ne se passe si je le fais.

+0

Fonctionne bien pour moi. – DVarga

+0

L'exception de pointeur nul est parce que l'habillage n'a pas été installé à ce stade. Pour que cela se produise, au minimum, vous avez besoin que la table fasse partie d'une scène et que CSS ait été appliqué (que vous pouvez forcer avec 'table.applyCSS()'); Je n'arrive toujours pas à le faire fonctionner, même avec ça, cependant. –

+0

@DVarga Je suis désolé ma question n'était pas complètement clair, le code que j'ai posté n'a pas jeté l'exception. Je l'ai corrigé. –

Répondre

0

Ainsi, la différence est que vous avez ajouté

try 
{ 
    fitColumns(); 
} 
catch (Exception e) { 
    e.printStackTrace(); 
} 

dans setMainApp méthode de votre contrôleur. Comme cela a été signalé par James_D, l'exception de pointeur nul est levée car l'habillage n'a pas été installé à ce point.

Vous pouvez contourner ce avec par exemple mettre à jour la méthode setMainApp dans le contrôleur comme:

public void setMainApp(MainApp mainApp) { 
    this.mainApp = mainApp; 
    tableView.setItems(mainApp.getPersonData()); 
    if(mainApp.primaryStage.isShowing()) 
     fitColumns(); 
    else { 
     mainApp.primaryStage.showingProperty().addListener((obs, oldVal, newVal) -> { 
      if(newVal) 
       fitColumns(); 
     }); 
    } 
} 

Cela vérifiera que le Stage de votre Application est affiché, et si oui, il se glisse les colonnes. S'il n'est pas affiché, il a attaché un écouteur au showingProperty du Stage donc dès que le Stage est affiché, la méthode fitColumns sera appelée.

+0

Votre solution fonctionne très bien, merci @DVarga :) –

+0

... mais mes problèmes ne sont pas terminés. Lorsque j'ai enveloppé PersonTable dans une disposition Parent (RootLayout) le même problème a refait surface. J'ai déjà posté une question [ici] (http://stackoverflow.com/questions/39327969/programmatically-resize-tablecolumns-in-javafx). Toute aide est la bienvenue, merci :) –