我想向JavaFX菜单添加选项列表。显示菜单时,应该修改选项的顺序(我将使用模糊匹配,但是该方法与我的问题无关)。我可以用分别包含CustomMenuItem
和TextField
的{{1}}来模仿这种行为:
ListView
但是,在菜单结构中包含import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.ListView;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuButton;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MWE extends Application {
@Override
public void start(Stage primaryStage) {
final Menu menu = new Menu("MENU");
final List<String> options = Arrays.asList(
"AbC",
"dfjksdljf",
"skdlfj",
"stackoverflow");
final StringProperty currentSelection = new SimpleStringProperty(null);
final TextField fuzzySearchField = new TextField(null);
final CustomMenuItem fuzzySearchItem = new CustomMenuItem(fuzzySearchField, false);
// TODO unfortunately we seem to have to grab focus like this!
fuzzySearchField.addEventFilter(MouseEvent.MOUSE_MOVED, e->{fuzzySearchField.requestFocus(); fuzzySearchField.selectEnd();});
final ObservableList<String> currentMatches = FXCollections.observableArrayList();
// just some dummy matching here
fuzzySearchField.textProperty().addListener((obs, oldv, newv) -> currentMatches.setAll(options.stream().filter(s -> s.toLowerCase().contains(newv)).collect(Collectors.toList())));
final ListView<String> lv = new ListView<>(currentMatches);
lv.addEventFilter(MouseEvent.MOUSE_MOVED, e -> lv.requestFocus());
final CustomMenuItem lvItem = new CustomMenuItem(lv, false);
menu.getItems().setAll(fuzzySearchItem, lvItem);
fuzzySearchField.textProperty().addListener((obs, oldv, newv) -> currentSelection.setValue(currentMatches.size() > 0 ? currentMatches.get(0) : null));
fuzzySearchField.setText("");
menu.setOnShown(e -> fuzzySearchField.requestFocus());
final MenuButton button = new MenuButton("menu");
button.getItems().setAll(menu);
Platform.runLater(() -> {
final Scene scene = new Scene(button);
primaryStage.setScene(scene);
primaryStage.show();
});
}
}
感到很奇怪。这就是为什么我尝试使用ListView
而不是MenuItem
的原因:
ListView
在该示例中,即使我看到import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.ListView;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MWE2 extends Application {
@Override
public void start(Stage primaryStage) {
final Menu menu = new Menu("MENU");
final List<String> options = Arrays.asList(
"AbC",
"dfjksdljf",
"skdlfj",
"stackoverflow");
final StringProperty currentSelection = new SimpleStringProperty(null);
final TextField fuzzySearchField = new TextField(null);
final CustomMenuItem fuzzySearchItem = new CustomMenuItem(fuzzySearchField, false);
// TODO unfortunately we seem to have to grab focus like this!
fuzzySearchField.addEventFilter(MouseEvent.MOUSE_MOVED, e->{fuzzySearchField.requestFocus(); fuzzySearchField.selectEnd();});
final ObservableList<String> currentMatches = FXCollections.observableArrayList();
// just some dummy matching here
fuzzySearchField.textProperty().addListener((obs, oldv, newv) -> currentMatches.setAll(options.stream().filter(s -> s.toLowerCase().contains(newv)).collect(Collectors.toList())));
currentMatches.addListener((ListChangeListener<String>)change -> {
List<MenuItem> items = new ArrayList<>();
items.add(fuzzySearchItem);
currentMatches.stream().map(MenuItem::new).forEach(items::add);
System.out.println("Updating menu items!");
menu.getItems().setAll(items);
});
fuzzySearchField.textProperty().addListener((obs, oldv, newv) -> currentSelection.setValue(currentMatches.size() > 0 ? currentMatches.get(0) : null));
fuzzySearchField.setText("");
menu.setOnShown(e -> fuzzySearchField.requestFocus());
final MenuButton button = new MenuButton("menu");
button.getItems().setAll(menu);
Platform.runLater(() -> {
final Scene scene = new Scene(button);
primaryStage.setScene(scene);
primaryStage.show();
});
}
}
打印到控制台,菜单也不会在显示时更新,因此"Updating menu items!"
的项目正在更新。但是,屏幕上的菜单不会更改。
有没有一种方法要求重新绘制菜单?
相关问题:
Menu
似乎没有方法menu
答案 0 :(得分:1)
我按照建议@Enigo和@Jesse_mw使用了自定义节点。我决定使用仅包含ListView
的{{1}},而不是VBox
,因为我只需要基本功能,并且不希望有其他令人困惑的处理程序或突出显示。还请注意,@kleopatra指出,不幸的是,dynamic updates of items works out of the box for ContextMenu
(and a potential bug in Menu
)并不是我的用例的适当选择。
这是我的最低工作示例代码:
Label
答案 1 :(得分:0)
要正确地动态更新JavaFX中的列表,可以像在代码中那样将Binding与可观察列表一起使用。以下代码的唯一问题是,由于Menu类的功能,它将无法按预期运行,每次更新列表时,菜单都会隐藏。我认为您应该像评论中讨论的那样对列表视图进行样式设置,并再次使用以下绑定,因为它适用于使用我相信的所有可观察列表的视图。
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.lang.management.PlatformManagedObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MWE2 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
final Menu menu = new Menu("MENU");
final MenuButton button = new MenuButton("menu");
final List<String> options = Arrays.asList(
"AbC",
"dfjksdljf",
"skdlfj",
"stackoverflow");
final StringProperty currentSelection = new SimpleStringProperty(null);
final TextField fuzzySearchField = new TextField(null);
final CustomMenuItem fuzzySearchItem = new CustomMenuItem(fuzzySearchField, false);
// TODO unfortunately we seem to have to grab focus like this!
fuzzySearchField.addEventFilter(MouseEvent.MOUSE_MOVED, e -> {
fuzzySearchField.requestFocus();
fuzzySearchField.selectEnd();
});
final ObservableList<String> currentMatches = FXCollections.observableArrayList();
// just some dummy matching here
fuzzySearchField.textProperty().addListener((obs, oldv, newv) -> {
currentMatches.setAll(options.stream().filter(s -> s.toLowerCase().contains(newv)).collect(Collectors.toList()));
});
//changed from ArrayList to ObservableArray
ObservableList<MenuItem> items = FXCollections.observableArrayList();
currentMatches.addListener((ListChangeListener<String>) change -> {
items.clear();//Clearing items to in-case of duplicates and NULL duplicates.
items.add(fuzzySearchItem);
currentMatches.stream().map(MenuItem::new).forEach(items::add);
System.out.println("Updating menu items!");
menu.getItems().setAll(items);
});
// Binding to Observable items.
Bindings.bindContent(menu.getItems(), items);
fuzzySearchField.textProperty().addListener((obs, oldv, newv) -> currentSelection.setValue(currentMatches.size() > 0 ? currentMatches.get(0) : null));
fuzzySearchField.setText("");
button.getItems().setAll(menu);
final Scene scene = new Scene(button);
primaryStage.setScene(scene);
primaryStage.show();
}
}