JavaFXListView自定义单元格

JavaFXListView自定义单元格,listview,javafx,fxml,Listview,Javafx,Fxml,我尝试为ListView创建自定义单元格,但没有显示任何内容 在我的菜单控制器中,我使用的ListView必须包含以下项目: FXML只是一个要测试的空白页: <AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHe

我尝试为ListView创建自定义单元格,但没有显示任何内容

在我的菜单控制器中,我使用的ListView必须包含以下项目:

FXML只是一个要测试的空白页:

<AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="50.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.exalow.easymind.ui.factory.ProjectCell" />
FXML是一个基本的空白锚地窗格:

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

<?import javafx.scene.layout.AnchorPane?>


<AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="50.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.exalow.easymind.ui.controllers.ProjectCellController" />

你的代码有很多问题

您正在为每个单元格分配相同的单元格实例,因为您的单元格工厂每次调用时都返回相同的实例。这违反了UI元素只能添加到场景图一次的规则。 如果在空单元格上调用updateItem,则将图形设置为null。在任何情况下,您都不会将图形设置回任何其他内容。因此,如果某个单元格曾经用作空单元格,并且这种情况通常在首次创建或显示ListView时发生,则该单元格将始终没有内容,即使它后来变为非空。 控制器通常不应该是UI组件。此规则有一些例外情况,例如使用,但这通常是一个坏主意,因为它会混淆控制器和视图。 因此,这里的策略应该是将单元工厂设置为创建新单元实例的工厂。该单元格可以在构造函数中加载FXML文件,这样每个单元格都会加载UI的新实例,但每个单元格只加载一次。在updateItem方法中,如果单元格为空,则将图形设置为null,否则根据项目配置控制器,并将图形设置为从FXML加载的UI

比如:

public class ProjectCellController  {
 
 
    @FXML
    private AnchorPane root;
 
    @FXML
    private ImageView imageView;
 
    @FXML
    private Label nameLabel;
 
    @FXML
    private Label descriptionLabel;
 
    @FXML
    private Label createDateLabel;
 
    public void setProject(Project project) {
        if (project.hasImage()) {
            imageView.setImage(new Image(project.getImageUrl()));
        } else imageView.setImage(new Image(project.getDefaultImage()));
 
        nameLabel.setText(project.getName());
        descriptionLabel.setText(project.getDescription());
        createDateLabel.setText(project.getCreateDate().toString());
    }
}
然后

以下是一个简化但完整的示例:

Project.java:

public class Project {
    private final String name ;
    public Project(String name) {
        this.name = name ;
    }
    public String getName() {
        return name ;
    }
}
Main.fxml:

MainController.java:

import javafx.fxml.FXML;
import javafx.scene.control.ListView;

public class MainController {

    @FXML
    private ListView<Project> list ;
    
    public void initialize() {
        for (int i = 1 ; i <= 100 ; i++) {
            list.getItems().add(new Project("Project "+i));
        }
        list.setCellFactory(lv -> new ProjectCell());
    }
}
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class ProjectCellController {

    @FXML
    private Label nameLabel ;
    
    public void setProject(Project project) {
        nameLabel.setText(project.getName());
    }
}
以及主要应用程序类:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;


public class App extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        Scene scene = new Scene(FXMLLoader.load(getClass().getResource("Main.fxml")), 640, 480);
        stage.setScene(scene);
        stage.show();
    }


    public static void main(String[] args) {
        launch();
    }

}

请在问题中发布看起来不像控制器的控制器和FXML,而不是作为单独的链接。这看起来像是你在每个单元格中尝试使用相同的UI实例,这肯定不起作用。我已经按照你说的做了,但像以前一样没有错误,但什么都没有出现。。。IDK怎么办:@Exalow那么你需要在问题中发布一个实际值,以便其他人可以复制、粘贴并运行它来查看问题。@Exalow它对我来说很好:我发布了一个完整的示例,你可以测试它。没有人能真正帮助你,如果没有一个完整的例子,你需要发布一个完整的例子,我们可以复制和运行。我不知道你有什么不明白的。我在我的回答中发布了一个完整的示例,您可以复制并运行它,并验证它是否有效。我不知道你还指望其他人做什么。哦,天哪。。。3个小时后,我发现这是因为我的列表视图是最终的,我想哭。感谢您的帮助:
public class ProjectCell extends ListCell<Project> {

    private AnchorPane root ;
    private ProjectCellController controller ;

    public ProjectCell() {

        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/fxml/ProjectCell.fxml"));
            root = loader.load();
            controller = loader.getController();
        } catch (IOException exc) {
            // this is basically fatal, so just bail here:
            throw new RuntimeException(exc);
        }
    }

    @Override
    protected void updateItem(Project project, boolean empty) {
        super.updateItem(project, empty);
        if (empty || project == null) {
            setGraphic(null);
        } else {
            controller.setProject(project);
            setGraphic(root);
        }
    }
}
projectListView.setCellFactory(listView -> new ProjectCell());
public class Project {
    private final String name ;
    public Project(String name) {
        this.name = name ;
    }
    public String getName() {
        return name ;
    }
}
import javafx.fxml.FXML;
import javafx.scene.control.ListView;

public class MainController {

    @FXML
    private ListView<Project> list ;
    
    public void initialize() {
        for (int i = 1 ; i <= 100 ; i++) {
            list.getItems().add(new Project("Project "+i));
        }
        list.setCellFactory(lv -> new ProjectCell());
    }
}
import java.io.IOException;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.ListCell;

public class ProjectCell extends ListCell<Project> {

    private Parent root ;
    private ProjectCellController controller ;
    
    public ProjectCell() {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("ProjectCell.fxml"));
            root = loader.load();
            controller = loader.getController() ;
        } catch (IOException exc) {
            throw new RuntimeException(exc);
        }
    }
    
    @Override
    protected void updateItem(Project project, boolean empty) {
        super.updateItem(project, empty);
        if (empty || project==null) {
            setGraphic(null);
        } else {
            controller.setProject(project);
            setGraphic(root);
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>

<VBox xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ProjectCellController">
    <children>
        <Label fx:id="nameLabel" />
    </children>
</VBox>
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class ProjectCellController {

    @FXML
    private Label nameLabel ;
    
    public void setProject(Project project) {
        nameLabel.setText(project.getName());
    }
}
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;


public class App extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        Scene scene = new Scene(FXMLLoader.load(getClass().getResource("Main.fxml")), 640, 480);
        stage.setScene(scene);
        stage.show();
    }


    public static void main(String[] args) {
        launch();
    }

}