在 setCellFactory Java8 中绑定两个 ListViews



我有两个列表视图,plantImagesallPlantsplantImages拥有植物的所有图像,allPlants拥有具有有关植物的所有信息的植物对象。我想在一个场景中列出它们,以便在从plantImage元素中检索到的植物名称前面显示植物的图像。图像及其各自的植物对象在两个列表中按相同的顺序排列。我已经工作过的代码,但有很多错误,例如当某些人单击单元格消失并且列表重新排序的植物的图像或名称时。我需要可以将两个列表绑定在一起的东西。到目前为止,我拥有的代码是:

原子序数计算 lambda 表达式运行的次数,其使用方式与某人在循环中使用迭代器i来处理两个列表的意义相同。问题是它会在每个点击事件上更新。 有没有更清洁的方法来实现这一点?

class Employee{
String name;
int age;
}
class controller{
ListView <ImageView>images = importImages();
ListView <Employee> employees = importEmployees();
}
AtomicInteger runCount= new AtomicInteger(0);
employees.setCellFactory(param -> new ListCell <Employee>() {
private ImageView imageview = new ImageView();
@Override
public void updateItem(Employee e, boolean empty) {
super.updateItem(e, empty);
if (empty) {
setText("empty");
setGraphic(null);
}else {
imageview.setImage(images.get(runCount.get()).getImage());
setText(employees.get(runCount.get()).toString());
setGraphic(imageview);
runCount.getAndIncrement();
}
}
});

首选方法是创建一个封装对象及其图像的类。此类的作用域只需限定为控制器(如果未使用 FXML,则定义列表视图的类(:

import java.util.ArrayList;
import java.util.List;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class Controller {
@FXML
private ListView<EmployeeWithImage> listView ;
private List<Employee> employees ;
private List<Image> images ;
public Controller() {
// presumably in real life these are populated elsewhere...
employees = new ArrayList<>();
images = new ArrayList<>();
for (int i = 1 ; i <=40 ; i++) {
employees.add(new Employee("Employee "+i, 20+i));
images.add(createImage(i));
}       
}
public void initialize() {

for (int i = 0 ; i < employees.size(); i++)  {
Image image = null ;
if (i < images.size()) {
image = images.get(i);
}
listView.getItems().add(new EmployeeWithImage(employees.get(i), image));
}
listView.setCellFactory(lv -> new ListCell<>() {
private final ImageView imageView = new ImageView();
{
setPrefHeight(100);
}
@Override
protected void updateItem(EmployeeWithImage employee, boolean empty) {
super.updateItem(employee, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(employee.getEmployee().getName());
imageView.setImage(employee.getImage());
setGraphic(imageView);
}
}
});
}
private Image createImage(int i) {
/// just a hack to get an image with a number in it
Label label = new Label(String.valueOf(i));
label.setMinSize(100, 100);
label.setMaxSize(100, 100);
label.requestLayout();
label.applyCss();
new Scene(label);
return label.snapshot(null, null);
}
private static class EmployeeWithImage {
private final Employee employee ;
private final Image image ;
public EmployeeWithImage(Employee employee, Image image) {
super();
this.employee = employee;
this.image = image;
}
public Employee getEmployee() {
return employee;
}
public Image getImage() {
return image;
}
}
}

另一种方法是将两个列表分开,并使用索引挂钩到图像列表:

import java.util.ArrayList;
import java.util.List;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class Controller {
@FXML
private ListView<Employee> listView ;
private List<Employee> employees ;
private List<Image> images ;
public Controller() {
// presumably in real life these are populated elsewhere...
employees = new ArrayList<>();
images = new ArrayList<>();
for (int i = 1 ; i <=40 ; i++) {
employees.add(new Employee("Employee "+i, 20+i));
images.add(createImage(i));
}       
}
public void initialize() {
listView.getItems().setAll(employees);
listView.setCellFactory(lv -> new ListCell<>() {
private final ImageView imageView = new ImageView();
{
setPrefHeight(100);
}
@Override
protected void updateItem(Employee employee, boolean empty) {
super.updateItem(employee, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(employee.getName());
int index = getIndex();
if (index >= 0 && index < images.size()) {
imageView.setImage(images.get(index));
setGraphic(imageView);
} else {
setGraphic(null);
}
}
}
});
}
private Image createImage(int i) {
/// just a hack to get an image with a number in it
Label label = new Label(String.valueOf(i));
label.setMinSize(100, 100);
label.setMaxSize(100, 100);
label.requestLayout();
label.applyCss();
new Scene(label);
return label.snapshot(null, null);
}

}

或者,仍然使用列表中的对象,您可以创建一个Map<Employee, Image>来查找图像:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class Controller {
@FXML
private ListView<Employee> listView ;
private List<Employee> employees ;
private List<Image> images ;
private Map<Employee, Image> employeeImageMap ;
public Controller() {
// presumably in real life these are populated elsewhere...
employees = new ArrayList<>();
images = new ArrayList<>();
for (int i = 1 ; i <=40 ; i++) {
employees.add(new Employee("Employee "+i, 20+i));
images.add(createImage(i));
}       
}
public void initialize() {
employeeImageMap = new HashMap<>();
for (int i = 0 ; i < Math.min(employees.size(), images.size()); i++) {
employeeImageMap.put(employees.get(i), images.get(i));
}
listView.getItems().setAll(employees);
listView.setCellFactory(lv -> new ListCell<>() {
private final ImageView imageView = new ImageView();
{
setPrefHeight(100);
}
@Override
protected void updateItem(Employee employee, boolean empty) {
super.updateItem(employee, empty);
if (empty || employee == null) {
setText(null);
setGraphic(null);
} else {
setText(employee.getName());
imageView.setImage(employeeImageMap.get(employee));
setGraphic(imageView);
}
}
});
}
private Image createImage(int i) {
/// just a hack to get an image with a number in it
Label label = new Label(String.valueOf(i));
label.setMinSize(100, 100);
label.setMaxSize(100, 100);
label.requestLayout();
label.applyCss();
new Scene(label);
return label.snapshot(null, null);
}

}

另一种方法是创建一个ListView<Integer>,用索引填充它,然后在updateItem()中使用索引:

import java.util.ArrayList;
import java.util.List;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class Controller {
@FXML
private ListView<Integer> listView ;
private List<Employee> employees ;
private List<Image> images ;
public Controller() {
// presumably in real life these are populated elsewhere...
employees = new ArrayList<>();
images = new ArrayList<>();
for (int i = 1 ; i <=40 ; i++) {
employees.add(new Employee("Employee "+i, 20+i));
images.add(createImage(i));
}       
}
public void initialize() {

for (int i = 0 ; i < employees.size(); i++) {
listView.getItems().add(i);
}
listView.setCellFactory(lv -> new ListCell<>() {
private final ImageView imageView = new ImageView();
{
setPrefHeight(100);
}
@Override
protected void updateItem(Integer index, boolean empty) {
super.updateItem(index, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(employees.get(index).getName());
if (index >= 0 && index < images.size()) {
imageView.setImage(images.get(index));
setGraphic(imageView);
} else {
setGraphic(null);
}
}
}
});
}
private Image createImage(int i) {
/// just a hack to get an image with a number in it
Label label = new Label(String.valueOf(i));
label.setMinSize(100, 100);
label.setMaxSize(100, 100);
label.requestLayout();
label.applyCss();
new Scene(label);
return label.snapshot(null, null);
}

}

最后一种方法是用对象填充列表视图,并覆盖updateIndex()方法,而不是updateItem()

import java.util.ArrayList;
import java.util.List;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class Controller {
@FXML
private ListView<Employee> listView ;
private List<Employee> employees ;
private List<Image> images ;
public Controller() {
// presumably in real life these are populated elsewhere...
employees = new ArrayList<>();
images = new ArrayList<>();
for (int i = 1 ; i <=40 ; i++) {
employees.add(new Employee("Employee "+i, 20+i));
images.add(createImage(i));
}       
}
public void initialize() {
listView.getItems().setAll(employees);
listView.setCellFactory(lv -> new ListCell<>() {
private final ImageView imageView = new ImageView();
{
setPrefHeight(100);
}
@Override
public void updateIndex(int index) {
super.updateIndex(index);
if (index < 0 || index > employees.size()) {
setText(null);
setGraphic(null);
} else {
setText(employees.get(index).getName());
if (index >= 0 && index < images.size()) {
imageView.setImage(images.get(index));
setGraphic(imageView);
} else {
setGraphic(null);
}
}
}
});
}
private Image createImage(int i) {
/// just a hack to get an image with a number in it
Label label = new Label(String.valueOf(i));
label.setMinSize(100, 100);
label.setMaxSize(100, 100);
label.requestLayout();
label.applyCss();
new Scene(label);
return label.snapshot(null, null);
}

}

请注意,所有这些都假定列表在显示期间是固定的。如果不是这种情况,处理动态列表的最简单方法是使用第一种方法。对对象和图像使用ObservableList,向它们注册侦听器,并在列表视图的项目发生更改时相应地更新它们。

为了完整起见,这里有一个应用程序类、Employee类和 FXML 文件来测试这一点。以下任何方法都适用于以下两个文件:

public class Employee {
private final String name ;
private final int age ;
public Employee(String name, int age) {
this.name = name ;
this.age = age ;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}

}
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
* JavaFX App
*/
public class App extends Application {

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

public static void main(String[] args) {
launch();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<BorderPane xmlns="http://javafx.com/javafx/14" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.listview.Controller">
<center>
<ListView fx:id="listView"/>
</center>
</BorderPane>

最新更新