ListView - 如何正确配置单元格的显示?

时间:2017-09-12 10:32:44

标签: java listview javafx javafx-8

我想自定义ListView单元格的显示。假设有一个ListView包含Person类的对象和两个按钮:

  • 添加(生成)新的Person
  • 删除所选的Person

我想实现以下目标:

  1. 如果未选择某个单元格,则必须显示getBasicView() Person字段的name
  2. 选择单元格后,必须显示getExpandedView()的{​​{1}},这是一个多行文字Person
  3. 问题是什么?

    我写的代码填写了上面给出的要求,但出现了其他错误:

    1. 当眨眼间添加新name + "/n" + surname时,单元格的显示会更改为未实现的Person方法(因此用户会看到toString()类似垃圾)。< / LI>

      Bug no.1

      1. 从最后一个单元格中移除sample.Person@c4f324时,奇怪的事情开始发生。已删除的Person Person仍保留在向下移动两个单元格的单元格中,因为它不再包含name对象 - 无法清除它。
      2. Bug no.2

        我尝试向单元格Person添加侦听器,可以检查项目是否为itemProperty,之后我可以将文本设置为null但不幸的是它不起作用。有谁知道如何让我的代码完全正常运行?

        提供SSCCE(假设所有文件都在""包中):

        Main.java:

        sample

        sample.fxml:

        package sample;
        
        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 Main extends Application {
        
          public void start(Stage stage) {
            FXMLLoader fxmlLoader = new FXMLLoader();
            fxmlLoader.setController(Controller.class);
            try {
              Parent parent = FXMLLoader.load(getClass().getResource("/sample/sample.fxml"));
              Scene scene = new Scene(parent);
              stage.setScene(scene);
              stage.show();
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        
          public static void main(String[] args) {
            launch();
          }
        }
        

        Controller.java:

        <?import javafx.scene.layout.HBox?>
        <?import javafx.scene.control.Button?>
        <?import javafx.scene.control.ListView?>
        <?import javafx.scene.layout.VBox?>
        
        <HBox xmlns:fx="http://javafx.com/fxml" fx:controller="sample.Controller">
            <VBox>
                <Button text="Add Person" onAction="#addPerson"/>
                <Button text="Delete Person" onAction="#deletePerson"/>
            </VBox>
            <ListView prefHeight="400" prefWidth="400" fx:id="listView"/>
        </HBox>
        

        Person.java:

        package sample;
        
        import javafx.application.Platform;
        import javafx.collections.FXCollections;
        import javafx.collections.ObservableList;
        import javafx.fxml.FXML;
        import javafx.scene.control.ListView;
        import javafx.scene.control.MultipleSelectionModel;
        import javafx.scene.control.cell.TextFieldListCell;
        import java.util.concurrent.ThreadLocalRandom;
        
        public class Controller {
        
          @FXML
          private ListView<Person> listView;
          private ObservableList<Person> personList = FXCollections.observableArrayList();
          private String [] names = {"John", "Katherine", "Michael", "August", "Peter"};
          private String [] surnames = {"Jones", "Mayer", "Stevens", "Wayne", "Milton"};
        
          @FXML
          private void initialize() {
            initializeListCells();
            initializeList();
          }
        
          private void initializeList() {
            for (int i = 0 ; i < 5 ; i++) {
              personList.add(generatePerson());
            }
            listView.setItems(personList);
          }
        
          private void initializeListCells() {
            listView.setCellFactory(param -> {
              TextFieldListCell<Person> cell = new TextFieldListCell<Person>();
              cell.selectedProperty().addListener((observable, oldValue, newValue) -> handleCellDisplaying(cell));
              cell.itemProperty().addListener((observable, oldValue, newValue) -> handleCellDisplaying(cell));
              return cell;
            });
          }
        
          private void handleCellDisplaying(TextFieldListCell<Person> cell) {
            Person person = cell.getItem();
            if (person != null) {
              Platform.runLater(() -> {
                if (!cell.isSelected()) {
                  cell.setText(person.getBasicView());
                } else {
                  cell.setText(person.getExpandedView());
                }
              });
            } else {
              cell.setText("");
            }
          }
        
          @FXML
          private void addPerson() {
            personList.add(generatePerson());
          }
        
          @FXML
          private void deletePerson() {
            MultipleSelectionModel<Person> selectionModel = listView.getSelectionModel();
            if (!selectionModel.isEmpty()) {
              int selectedIndex = selectionModel.getSelectedIndex();
              personList.remove(selectedIndex);
            }
          }
        
          private Person generatePerson() {
            int nameRandom = ThreadLocalRandom.current().nextInt(1,5);
            int surnameRandom = ThreadLocalRandom.current().nextInt(1,5);
            return new Person(names[nameRandom],surnames[surnameRandom]);
          }
        }
        

1 个答案:

答案 0 :(得分:1)

问题是TextFieldListCell实现了单元格生命周期方法(updateItem等)并在各个点有效地调用setText(item.toString())。这显然会干扰您尝试实施的行为。 (例如,删除最后一个单元格的问题似乎发生,因为setText("")在之前被调用 TextFieldListCell将文本重置为其先前的值。如果您使用Platform.runLater(...) 1}} hack以便它包围完整的if - else子句,然后这个问题就消失了。但是......)

如果您不需要单元格可编辑,则无需使用TextFieldListCell:只需使用普通ListCell。 (另请注意,处理程序中不需要Platform.runLater(...)更改项目/选定状态。)

package sample;

import java.util.concurrent.ThreadLocalRandom;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MultipleSelectionModel;

public class Controller {

    @FXML
    private ListView<Person> listView;
    private ObservableList<Person> personList = FXCollections.observableArrayList();
    private String[] names = { "John", "Katherine", "Michael", "August", "Peter" };
    private String[] surnames = { "Jones", "Mayer", "Stevens", "Wayne", "Milton" };

    @FXML
    private void initialize() {
        initializeListCells();
        initializeList();
    }

    private void initializeList() {
        for (int i = 0; i < 5; i++) {
            personList.add(generatePerson());
        }
        listView.setItems(personList);
    }

    private void initializeListCells() {
        listView.setCellFactory(param -> {
            ListCell<Person> cell = new ListCell<Person>();
            cell.selectedProperty().addListener((observable, oldValue, newValue) -> handleCellDisplaying(cell));
            cell.itemProperty().addListener((observable, oldValue, newValue) -> handleCellDisplaying(cell));
            return cell;
        });
    }

    private void handleCellDisplaying(ListCell<Person> cell) {
        Person person = cell.getItem();
        if (person != null) {
            if (!cell.isSelected()) {
                cell.setText(person.getBasicView());
            } else {
                cell.setText(person.getExpandedView());
            }
        } else {
            cell.setText("");
        }
    }

    @FXML
    private void addPerson() {
        personList.add(generatePerson());
    }

    @FXML
    private void deletePerson() {
        MultipleSelectionModel<Person> selectionModel = listView.getSelectionModel();
        if (!selectionModel.isEmpty()) {
            int selectedIndex = selectionModel.getSelectedIndex();
            personList.remove(selectedIndex);
        }
    }

    private Person generatePerson() {
        int nameRandom = ThreadLocalRandom.current().nextInt(1, 5);
        int surnameRandom = ThreadLocalRandom.current().nextInt(1, 5);
        return new Person(names[nameRandom], surnames[surnameRandom]);
    }
}