web-dev-qa-db-fra.com

Personnaliser ListView dans JavaFX avec FXML

Je veux créer une vue de liste personnalisée dans javafx. Ici, je dois lier plusieurs composants dans la cellule de la liste comme suit, comme une étiquette, un champ de texte, un bouton sous une HBox et deux boutons, un hyperlien, une étiquette dans une autre HBox et ces HBox relèvent d'une VBox et cette VBox relève de la cellule de liste unique et il se répétera et fera une vue de liste.

Le code est

<ListView fx:id="ListView" layoutX="0" layoutY="30" prefWidth="600" prefHeight="300">
    <HBox fx:id="listBox" alignment="CENTER_LEFT">
        <padding><Insets top="5" bottom="5" left="5"></Insets> </padding>
        <HBox alignment="CENTER_LEFT" prefWidth="170" minWidth="88">
            <Label fx:id="surveyName" text="Field A" styleClass="Name"></Label>
        </HBox>
        <VBox styleClass="Description" prefWidth="155" minWidth="86">

            <HBox>
                <HBox styleClass="surveyDesIcon" prefWidth="20" prefHeight="16"></HBox>
                <Label fx:id="surveyCode" text="PRW3456HJ"></Label>
            </HBox>
            <HBox>
                <HBox styleClass="DateIcon" prefWidth="20" prefHeight="16"></HBox>
                <Label fx:id="Date" text="PRW3456HJ"></Label>
            </HBox>
        </VBox>
        <HBox fx:id="Status" prefWidth="160" minWidth="80">
            <Label fx:id="StatusLabel" text="Checking Files.."/>
        </HBox>
        <HBox fx:id="StatusIcon1" prefWidth="50" prefHeight="50" alignment="CENTER">
            <Label styleClass="StatusIcon1" prefWidth="24" prefHeight="24" alignment="CENTER"/>
        </HBox>
        <HBox fx:id="StatusIcon2" prefWidth="50" prefHeight="50" styleClass="StatusIconBox" alignment="CENTER">
            <Hyperlink styleClass="StatusIcon2" prefWidth="24" maxHeight="24" alignment="CENTER"/>
        </HBox>
    </HBox>
</ListView>
35
Santosh Biswakarma

Je comprends ta question. Il existe principalement deux façons de définir des éléments dans un Listview:

1. Créez le ObservableList et définissez les éléments du ListView avec le ObservableList (listView.setItems(observableList)).

2. Utilisez la méthode setCellFactory() de la classe ListView.

Vous préféreriez utiliser la méthode setCellFactory(), car cette approche simplifie le processus et permet de séparer la logique métier et l'interface utilisateur (FXML).


Voici une explication plus détaillée:


1. Créez un nouveau fichier FXML avec le nom listview.fxml Pour contenir la ListView, et définissez la classe ListViewController comme contrôleur:

Fichier: listview.fxml :

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.ListView?>
<?import demo.ListViewController?>

<GridPane xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
     <ListView fx:id="listView"/>
</GridPane>

2. Créez le contrôleur et nommez-le ListViewController.
Le contrôleur peut charger le fichier listview.fxml Et accéder au listview.

Fichier: ListViewController.Java :

package demo;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.util.Callback;
import Java.io.IOException;
import Java.util.Set;

public class ListViewController
{
    @FXML
    private ListView listView;
    private Set<String> stringSet;
    ObservableList observableList = FXCollections.observableArrayList();

    public ListViewController()
    {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listview.fxml"));
        fxmlLoader.setController(this);
        try
        {
            Parent parent = (Parent)fxmlLoader.load();
            Scene scene = new Scene(parent, 400.0 ,500.0);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void setListView()
    {
        stringSet.add("String 1");
        stringSet.add("String 2");
        stringSet.add("String 3");
        stringSet.add("String 4");
        observableList.setAll(stringSet);
        listView.setItems(observableList);
        listView.setCellFactory(new Callback<ListView<String>, javafx.scene.control.ListCell<String>>()
        {
            @Override
            public ListCell<String> call(ListView<String> listView)
            {
                return new ListViewCell();
            }
        });
    }
}

. Vous devez d'abord définir la valeur de ObservableList. C'est très important.
Ensuite, définissez les éléments de la liste à l'aide de ObservableList et appelez la méthode setCellFactory() sur ListView. Dans l'exemple donné, je prends simplement les valeurs String et les ajoute à l'ensemble String (le Set<String> stringSet).


4. Lorsque la méthode setCellFactory() est appelée sur le ListView, elle renverra le ListCell. Donc, par souci de simplicité, j'ai ajouté une classe qui étend la ListCell, et la méthode setGraphic() est présente pour la ListCell() et définira les éléments de la ListCell.

Fichier: ListViewCell.Java :

package demo;

import javafx.scene.control.ListCell;

public class ListViewCell extends ListCell<String>
{
    @Override
    public void updateItem(String string, boolean empty)
    {
        super.updateItem(string,empty);
        if(string != null)
        {
            Data data = new Data();
            data.setInfo(string);
            setGraphic(data.getBox());
        }
    }
}

5. Je viens d'ajouter une classe qui chargera le listCellItem.fxml Et renverra le HBox, qui contiendra les autres composants en tant qu'enfants.
Le HBox est alors défini sur le ListCell.

Fichier: listCellItem.fxml :

 <?import demo.Data?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.control.Label?>

<HBox xmlns:fx="http://javafx.com/fxml" fx:id="hBox">
<children>
    <Label  fx:id="label1"/>
    <Label  fx:id="label2"/>
</children>
</HBox>

Fichier: Data.Java :

package demo;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import Java.io.IOException;

public class Data
{
    @FXML
    private HBox hBox;
    @FXML
    private Label label1;
    @FXML
    private Label label2;

    public Data()
    {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listCellItem.fxml"));
        fxmlLoader.setController(this);
        try
        {
            fxmlLoader.load();
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void setInfo(String string)
    {
        label1.setText(string);
        label2.setText(string);
    }

    public HBox getBox()
    {
        return hBox;
    }
}

De cette façon, vous pouvez utiliser la méthode setCellFactory() pour séparer les éléments qui sont la logique métier et FXML.

J'espère que cela vous sera utile.

77
Anvay

L'exemple ci-dessus par @ Anvay a besoin de quelques ajustements pour fonctionner. Ce sont des choses simples à mettre sur la bonne voie.

  1. Le ListViewController doit être exécuté sur le thread d'application JavaFX.
  2. Vous ne pouvez appeler que les éléments @FXML injectés à partir de la méthode JavaFX initialize ()
  3. Besoin d'appeler setListView ()
  4. Le stringSet dans l'exemple doit être alloué avec un nouveau avant d'appeler setListView ().

Le ListViewController ci-dessous fonctionne avec ces modifications. J'ai changé " stringSet " en une liste, " stringList ". Le contrôleur est à peu près l'exemple de contrôleur fourni par Scene Builder 2

 public class ListViewController 
 {

     @FXML private   ResourceBundle      resources;

     @FXML private   URL                 location;

     @FXML private   ListView            listView;

     private         List<String>        stringList     = new ArrayList<>(5);
     private         ObservableList      observableList = FXCollections.observableArrayList();

     public void setListView(){

         stringList.add("String 1");
         stringList.add("String 2");
         stringList.add("String 3");
         stringList.add("String 4");

         observableList.setAll(stringList);

         listView.setItems(observableList);

         listView.setCellFactory(
             new Callback<ListView<String>, javafx.scene.control.ListCell<String>>() {
                 @Override
                 public ListCell<String> call(ListView<String> listView) {
                     return new ListViewCell();
                 }
             });
     }

     @FXML
     void initialize() {
         assert listView != null : "fx:id=\"listView\" was not injected: check your FXML file 'CustomList.fxml'.";

         setListView();
     }

 }//ListViewController

La plate-forme JavaFX doit être démarrée dans la méthode main () à partir d'une application JavaFX. Netbeans fournit facilement la plupart de cette structure à partir du modèle d'application Maven JavaFX.

public class MainApp extends Application {

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

        Parent root = FXMLLoader.load(getClass().getResource("/fxml/CustomList.fxml"));

        Scene scene = new Scene(root);
        scene.getStylesheets().add("/styles/Styles.css");

        stage.setTitle("CustomList");
        stage.setScene(scene);
        stage.show();
    }

    /**
     *  The main() method is ignored in correctly deployed JavaFX application.
     * 
     *  @param args the command line arguments
     **/
    public static void main(String[] args) {

        launch(args);
    }
}
4
will

La réponse d'Anvay pour une raison quelconque n'a pas fonctionné pour moi, ce que je devais faire pour y remédier n'était que de très petits ajustements:

  1. supprimer l'instruction d'importation de données de listCellItem.fxml
  2. comme commentaire sous les états de publication dans Data.Java, mettez hBox = fmxlLoader.load ()
  3. J'avais aussi une classe principale (intellij générée automatiquement).

    public class MainMain extends Application {
    
    @Override
    public void start(Stage primaryStage) throws Exception{
    
    FXMLLoader fxmlLoader = new 
    FXMLLoader(getClass().getResource("MainController.fxml"));
    try
    {
        Parent root = fxmlLoader.load();
    
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Title");
        primaryStage.show();
    }
    catch (IOException e)
    {
        throw new RuntimeException(e);
    }
    }
    
    
    
    public static void main(String[] args) {
        launch(args);
    }
    

Je sais que c'était probablement évident pour la plupart des experts ici, mais ces problèmes m'ont perplexe pendant des heures pendant que je déboguais.

2
Velo