如何在 TableView 中正确设置图像

How to correctly set images inside TableView

我正在尝试使用将 ImageView 作为其值之一的对象来填充表格视图,但只有表格视图中的最后一项显示图像,其余的 none

public class MainController implements Initializable {

    public TableView<Apple> table;
    public TableColumn nameColumn;
    public TableColumn imageColumn;

    ImageView imageView = new ImageView(new Image("download.jpg"));
    Apple apple = new Apple("Bob",imageView);
    Apple apple2 = new Apple("John",imageView);
    Apple[] apples = {apple2,apple};
    List<Apple> appleList = Arrays.asList(apples);
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        imageColumn.setCellValueFactory(new PropertyValueFactory<>("image"));
        nameColumn.setCellValueFactory((new PropertyValueFactory<>("name")));
        table.setItems(FXCollections.observableArrayList(appleList));
    }
}

public class Apple {

    private final int SIZE = 20;

    String name;
    ImageView image;

    public Apple(String name, ImageView image) {
        this.name = name;
        this.image = image;
        image.setFitHeight(SIZE);
        image.setFitWidth(SIZE);

    }

    public String getName() {
        return name;
    }

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

    public ImageView getImage() {
        return image;
    }

    public void setImage(ImageView image) {
        this.image = image;
    }
}

我不确定为什么会这样,不过我在网上看到有人使用循环将图像 1 对 1 设置。

如@jewelsea 的评论所述,您的模型 class (Apple) 应仅包含数据;它不应包含 UI 字段,例如 ImageView。任何 Node 只能在场景图中出现一次,这就是为什么您只能在 table.

的一行中看到 ImageView

您应该将图像的路径或 Image(仅是数据)本身存储在模型 class 中。这两种选择之间的权衡是计算时间与内存消耗的权衡。如果您存储图像的路径,则每次单元格更新时(例如在滚动期间)都需要加载图像,这需要时间。另一方面,如果您存储 Image,那么整个 table 所需的所有图像都需要存储在内存中,无论它们是否显示。

如果您的 table 只需要少量图像,我建议将 Image 存储在模型 class 中。如果 table 只有几行,或者图像数量较少且多行显示同一图像,则可能会发生这种情况。请注意,Images 可以 由多个 ImageViews 共享,因此无需多次加载任何单个 Image

在模型 class 中使用 Image 将如下所示:

public class Apple {

    private String name;
    private Image image;

    public Apple(String name, Image image) {
        this.name = name;
        this.image = image;
    }

    public String getName() {
        return name;
    }

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

    public Image getImage() {
        return image;
    }

    public void setImage(Image image) {
        this.image = image;
    }
}

您将需要一个单元格实现来显示图像:

public class ImageCell extends TableCell<Apple, Image> {

    private static final IMAGE_SIZE = 20 ;
    private final ImageView imageView ;

    public TableCell() {
        imageView = new ImageView();
        imageView.setFitWidth(IMAGE_SIZE);
        imageView.setFitHeight(IMAGE_SIZE);
        imageView.setPreserveRatio(true);
    }

   @Override
   protected void updateItem(Image image, boolean empty) {
        if (empty || item == null) {
            setGraphic(null);
        } else {
            imageView.setImage(image);
            setGraphic(imageView);
        }
   }
}

您的应用程序代码如下所示:

public class MainController implements Initializable {

    public TableView<Apple> table;
    public TableColumn<Apple, String> nameColumn;
    public TableColumn<Apple, Image> imageColumn;

    Image downloadImage = new Image("download.jpg");
    Apple apple = new Apple("Bob",downloadImage);
    Apple apple2 = new Apple("John",downloadImage);
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        imageColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue().getImage()));

        imageColumn.setCellFactory(column -> new ImageCell());

        nameColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName()));
        table.setItems(FXCollections.observableArrayList(apple2, apple));
    }
}

如果您在 table 中有大量不同的图像,这可能不太可能,您应该在模型中表示图像的路径 class 而不是:

public class Apple {

    private String name;
    private String imagePath;

    public Apple(String name, String imagePath) {
        this.name = name;
        this.imagePath = imagePath;
    }

    public String getName() {
        return name;
    }

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

    public String getImagePath() {
        return imagePath;
    }

    public void setImage(String imagePath) {
        this.imagePath = imagePath;
    }
}

然后你的单元格class需要加载图像:

public class ImageCell extends TableCell<Apple, String> {

    private static final IMAGE_SIZE = 20 ;
    private final ImageView imageView ;

    public TableCell() {
        imageView = new ImageView();
        imageView.setFitWidth(IMAGE_SIZE);
        imageView.setFitHeight(IMAGE_SIZE);
        imageView.setPreserveRatio(true);
    }

   @Override
   protected void updateItem(String imagePath, boolean empty) {
        if (empty || item == null) {
            setGraphic(null);
        } else {
            imageView.setImage(new Image(imagePath));
            setGraphic(imageView);
        }
   }
}

并且您的应用程序代码有明显的修改:

public class MainController implements Initializable {

    public TableView<Apple> table;
    public TableColumn<Apple, String> nameColumn;
    public TableColumn<Apple, String> imageColumn;

    Apple apple = new Apple("Bob", "download.jpg");
    Apple apple2 = new Apple("John", "download.jpg");
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        imageColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getImagePath()));

        imageColumn.setCellFactory(column -> new ImageCell());

        nameColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName()));
        table.setItems(FXCollections.observableArrayList(apple2, apple));
    }
}