0

编辑 ---显示对象类,在初始化方法中添加代码,删除以前的过滤器方法,并从 FXML 中删除 TextField 的 OnAction。---

我第一次尝试解决这个问题导致了许多 Android 问题,所以我在这里。

我使用 JavaFX、Scenebuilder 和 FXML 制作了一个简单的 GUI。此 GUI 模拟工厂车间的库存,并且可以从ObservableList. 它还应该根据String用户在 a 中输入的部分 s过滤对象TextField

这在该程序的前身中很简单,因为它都是通过命令行完成的;但是,我遇到了一个以前从未见过的新问题。

每个对象打印一个带有用户指定前缀的伪随机 ID 号(F 代表花,W 代表杂草,H 代表草本,Fn 代表真菌)、名称、颜色和布尔属性,例如“荆棘”、“香味” ,“可食用”等。这些由单选按钮确定。例如,如果用户输入一个新花的所有值,它会打印为:“ID:F-21,名称:玫瑰,颜色:红色,刺?假,香味?真”

这是我最初的 ObservableList 和 FilteredList:

ObservableList<Plant> observablePlantList = FXCollections.observableArrayList();
FilteredList<Plant> filteredList = new FilteredList<Plant>(observablePlantList);

这是我的 Plant 类和一个子类(有 4 个完全相同)以显示它们在列表中的显示方式(布尔值是单选按钮):

    public class Plant {
public String ID;
public String name;
public String color;
public boolean smell;
public boolean thorns;
public boolean edible;
public boolean poisonous;
public boolean flavor;
public boolean medicine;
public boolean seasonal;
public int idNum;

public Plant(String ID, int idNum, String name, String color, boolean smell, boolean thorns, boolean edible, boolean poisonous, boolean flavor, boolean medicine, boolean seasonal) {
    this.ID = ID;
    this.idNum = randomID(idNum);
    this.name = name;
    this.color = color;
    this.smell = false;
    this.thorns = false;
    this.edible = false;
    this.poisonous = false;
    this.flavor = false;
    this.medicine = false;
    this.seasonal = false;
}

public void setColor(String color) {this.color = color;}
public void setID(String ID) {this.ID = ID;}
public void setName(String name) {this.name = name;}

public String getID() {return ID;}
public String getName() {return name;}

public int randomID(int idNum) {
    Random randomNum = new Random();
    idNum = randomNum.nextInt(50);
    return idNum;
}

public String toString() {

    return "ID: " + this.ID + "-" + this.idNum + ", Name: " + this.name + ", Color: " + this.color;
}

}

子类

    public class Flower extends Plant {

public Flower(String ID, int idNum, String name, String color, boolean smell, boolean thorns, boolean edible, boolean poisonous, boolean flavor, boolean medicine, boolean seasonal) {

    super(ID, idNum, name, color, smell, thorns, edible, poisonous, flavor, medicine, seasonal);
}
public void setSmell(boolean smell) {
    this.smell = smell;
}
public void setThorns(boolean thorns) {
    this.thorns = thorns;
}
public String toString() {

    return super.toString() + ", Scent? " + this.smell + ", Thorns? " + this.thorns;
}

}

由于下面答案中的建议,这就是我在初始化方法中的内容。问题是没有任何改变:

    @Override
public void initialize(URL fxmlFileLocation, ResourceBundle rb) {

    plantList.setItems(filteredList);

    //binding filterInput text entries
    filteredList.predicateProperty().bind(Bindings.createObjectBinding(() -> {
        String filterText = filterInput.getText();
        if(filterText == null || filterText.isEmpty()) {
            return null;
        }
        else {
            final String uppercase = filterText.toUpperCase();
            return (plant) -> plant.getName().toUpperCase().contains(uppercase);
        }
    }, filterInput.textProperty()));

我的目标是实时过滤 ObservableArrayList,而不是使用一个按钮来过滤和显示新场景中的项目。我已经看到了这个实现,并且非常喜欢这个功能。

感谢您提供的任何帮助。

4

2 回答 2

1

你的代码有什么问题:

  • onAction您在事件处理程序中注册侦听器,TextField而不是Initializable.initialize在控制器的方法中。这意味着侦听器将在Action事件发生时注册(默认情况下,当您点击时ENTER),并且每次事件发生时都会注册一个新的侦听器Action
  • 您分配filterItems给您的items属性ListView,但如果文本变短,则在再次过滤之前不要分配新值。这意味着plantList.getItems()(您迭代的列表)可以与您向 => 添加项目的列表相同ConcurrentModificationException
  • 你永远不会清除过滤列表

更优雅的方式

使用FilteredList. 这是一个旨在为您进行过滤的类。

@FXML
private ListView<Plant> plantList;
@FXML
private TextField filterInput;

ObservableList<Plant> observablePlantList = FXCollections.observableArrayList();
FilteredList<Plant> filterItems = new FilteredList<>(observablePlantList);

@Override
public void initialize(URL url, ResourceBundle rb) {
    plantList.setItems(filterItems);
    
    // bind predicate to text filterInput text
    filterItems.predicateProperty().bind(javafx.beans.binding.Bindings.createObjectBinding(() -> {
        String text = filterInput.getText();
        if (text == null || text.isEmpty()) {
            return null;
        } else {
            final String uppercase = text.toUpperCase();
            return (plant) -> plant.getName().toUpperCase().contains(uppercase);
        }
    }, filterInput.textProperty()));
}

当然,在这种情况下,您会从 fxml 文件中删除该onAction属性。TextField

于 2015-11-24T10:15:10.997 回答
0

我需要学习引入新版本 JavaFX 的新方法。我考虑了我真正想做的事情,把它写在纸上,并尽我的能力/目前的知识把它写下来。

这是工作代码,在初始化方法中仍然有一个 ChangeListener,并且 OnAction 仍然从 FXML 文件中删除。现在,当我输入一个字符时,相应的植物就会出现,并在按下退格键时显示原始植物。

对于没有使用您的建议,我真诚地道歉。我还没有足够的经验来完全理解谓词和绑定,但我肯定有一些东西要研究!

     public void filterPlantList(String oldValue, String newValue) {

    ObservableList<Plant> filteredList = FXCollections.observableArrayList();
    if(filterInput == null || (newValue.length() < oldValue.length()) || newValue == null) {
        plantList.setItems(observablePlantList);
    }
    else {
        newValue = newValue.toUpperCase();
        for(Plant plants : plantList.getItems()) {
            String filterText = plants.getName();
            if(filterText.toUpperCase().contains(newValue)) {
                filteredList.add(plants);
            }
        }
        plantList.setItems(filteredList);
    }
}
@Override
public void initialize(URL fxmlFileLocation, ResourceBundle rb) {

    //add Listener to filterInput TextField
    filterInput.textProperty().addListener(new ChangeListener() {
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {
            filterPlantList((String) oldValue, (String) newValue);
        }
    });
于 2015-11-26T00:25:42.923 回答