JavaFX ContextMenu项目在运行时不显示更改

时间:2018-04-12 16:26:08

标签: java javafx javafx-8 contextmenu extractor

所以我正在编写一个AutosuggestMenu,为TextField添加一个监听器,并在弹出窗口ContextMenu中推荐建议,基于将输入的击键与Collection进行比较提供的词语。
遗憾的是,ContextMenu元素的更改未显示,我怀疑这是因为我正在修改与ObservableList关联的ContextMenu元素,而不是列表本身。

浏览堆栈导致我相信我应该实现extractor,但根据提供的示例,我不知道如何针对我的具体问题执行此操作。任何解决方案都将非常感谢!

来源:

package com.sknb.gui;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import javafx.geometry.Side;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;


public class AutosuggestMenu {
    public final static int DEFAULT_MENU_SIZE = 10;

    //Data members
    private int menuSize;
    private Collection<String> wordList;
    //GUI members
    private ContextMenu menu;
    private final Text textBefore, textMatching, textAfter;


    public AutosuggestMenu(Collection<String> keyList) {
        this(keyList, DEFAULT_MENU_SIZE);
    }

    public AutosuggestMenu(Collection<String> keyList, int numEntries) {
        if (keyList == null) {
            throw new NullPointerException();
        }

        this.wordList = keyList;
        this.menuSize = numEntries;
        this.menu = new ContextMenu();

        for (int i = 0; i < this.menuSize; i++) {
            CustomMenuItem item = new CustomMenuItem(new Label(), true);
            this.menu.getItems().add(item);
        }

        this.textBefore = new Text();
        this.textMatching = new Text();
        this.textAfter = new Text();
    }

    public void addListener(TextField field) {
         field.textProperty().addListener((observable, oldValue, newValue) -> {
            String enteredText = field.getText();

            if (enteredText == null || enteredText.isEmpty()) {
                this.menu.hide();
            } else {
                List<String> filteredEntries = this.wordList.stream()
                        .filter(e -> e.contains(enteredText))
                        .collect(Collectors.toList());

                if (!filteredEntries.isEmpty()) {
                    populatePopup(field, filteredEntries, enteredText);

                    if (!(this.menu.isShowing())) {
                        this.menu.show(field, Side.BOTTOM, 0, 0); 
                    }
                } else {
                    this.menu.hide();
                }
            }
        });    

        field.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
            this.menu.hide();
        });
    }             

    private void populatePopup(TextField field, List<String> matches, String query) {
        int i = 0,
            max = (matches.size() > this.menuSize) ? this.menuSize : 
                                                 matches.size();

        for (MenuItem item : this.menu.getItems()) {
            if (i < max) {
                String result = matches.get(i);
                item.setGraphic(generateTextFlow(result, query));     
                item.setVisible(true);     

                item.setOnAction(actionEvent -> {
                    field.setText(result);
                    field.positionCaret(result.length());
                    this.menu.hide();
                });
            } else {
                item.setVisible(false);
            }

            i++;
        }
    }

    private TextFlow generateTextFlow(String text, String filter) {        
        int filterIndex = text.indexOf(filter);

        this.textBefore.setText(text.substring(0, filterIndex));
        this.textAfter.setText(text.substring(filterIndex + filter.length()));
        this.textMatching.setText(text.substring(filterIndex,  filterIndex + filter.length()));
        textMatching.setFill(Color.BLUE);
        textMatching.setFont(Font.font("Helvetica", FontWeight.BOLD, 12)); 

        return new TextFlow(textBefore, textMatching, textAfter);
    }

    public int getMenuSize() {
        return this.menuSize;
    }

    public void setMenuSize(int size) {
        this.menuSize = size;
    }

    public Collection<String> getKeyList() {
        return this.wordList;
    }

    public void setKeyList(Collection<String> keyList) {
        this.wordList = keyList;
    }

    //To do: add ways to change style of ContextMenu/menu items/text elements
}

使用text file of English words作为字典测试类:

package autosuggestfieldtest;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import com.sknb.gui.AutosuggestMenu;


public class AutosuggestFieldTest extends Application {
    private TextField textfield;
    private Collection<String> words;
    private AutosuggestMenu popup;

    @Override
    public void start(Stage primaryStage) {
        String filename = Paths.get("").toAbsolutePath().toString() + "\\words.txt";

        try (Stream<String> stream = Files.lines(Paths.get(filename))) {
            words = stream
                    .collect(Collectors.toSet());
        } catch (IOException e) {
            System.out.println("DERP");
        }

        popup = new AutosuggestMenu(words);
        textfield = new TextField();

        popup.addListener(textfield);

        StackPane root = new StackPane();
        root.getChildren().add(textfield);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("AutocompleteField Test");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}

注意:这是similar solution Ruslan的修改,但我想知道是否有一个解决方案不涉及每次按键清除/重新填充菜单?即只需使用setGraphic并刷新ContextMenu

0 个答案:

没有答案