9

私は(私の目には)ばかげた問題に出くわしました。ただし、これに対する解決策は見つかりません(おそらく、適切な検索キーワードを使用していないため、または簡単な場合は難しすぎるためです。)シナリオ:

500人の顧客がいるコンボボックスがあります。私は1人の貸衣装を選択する必要があります。

Swingでは、リストが表示されて入力を開始すると、入力した文字に自動的にジャンプします。例えば:

アイテム:

  • アダム
  • ダーク
  • フレディ
  • ..。
  • ロジャー
  • スティーブン
  • Z人

コンボボックスリストが開いているときは、「R」と入力するだけで、スイングで「R」で始まる最初の顧客にジャンプします。javafx 2では、その動作がないようです...有効にする必要があるオプションはありますか、または代わりに編集可能なfilter()コンボボックスを使用して、キーを押すたびに起動されるメソッドを作成する必要がありますか?

編集:ブーペンドラの答えに基づく解決策:

public class FilterComboBox<T> extends ComboBox<T> {
private final FilterComboBox<T> fcbo = this;

//private FilterComboBox fcbo = this;
private ObservableList<T> items;
private ObservableList<T> filter;
private String s;
private Object selection;

private class KeyHandler implements EventHandler< KeyEvent> {

    private SingleSelectionModel<T> sm;

    public KeyHandler() {
        sm = getSelectionModel();
        s = "";
    }

    @Override
    public void handle(KeyEvent event) {
        filter.clear();
        // handle non alphanumeric keys like backspace, delete etc
        if (event.getCode() == KeyCode.BACK_SPACE && s.length() > 0) {
            s = s.substring(0, s.length() - 1);
        } else {
            s += event.getText();
        }

        if (s.length() == 0) {
            fcbo.setItems(items);
            sm.selectFirst();
            return;
        }
        //System.out.println(s);
        if (event.getCode().isLetterKey()) {
            for (T item : items) {
                if (item.toString().toUpperCase().startsWith(s.toUpperCase())) {

                    filter.add(item);
                    //System.out.println(item);

                    fcbo.setItems(filter);

                    //sm.clearSelection();
                    //sm.select(item);

                }
            }
            sm.select(0);
        }

    }
}

public FilterComboBox(final ObservableList<T> items) {
    super(items);
    this.items = items;
    this.filter = FXCollections.observableArrayList();

    setOnKeyReleased(new KeyHandler());

    this.focusedProperty().addListener(new ChangeListener() {
        @Override
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {
            if (newValue == false) {
                s = "";
                fcbo.setItems(items);
                fcbo.getSelectionModel().select((T)selection);
            }

        }
    });

    this.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
        @Override
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {
            if (newValue != null) {
                selection = (Object) newValue;
            }

        }
    });
}

}

4

4 に答える 4

7

フィルタコンボボックスの最も単純な形式は、次のコードのようになります。しかし、それを洗練するためにはもっと多くの作業が必要になるでしょう。また、あなたの場合のようにリストが膨大な場合は、キーを押すたびにコレクション全体をループするため、パフォーマンスの問題が発生する可能性があります。

public class FilterComboBox extends ComboBox< String > {
    private ObservableList< String >    items;

    private class KeyHandler implements EventHandler< KeyEvent > {

        private SingleSelectionModel< String >  sm;
        private String                          s;

        public KeyHandler() {
            sm = getSelectionModel();
            s = "";
        }

        @Override
        public void handle( KeyEvent event ) {
            // handle non alphanumeric keys like backspace, delete etc
            if( event.getCode() == KeyCode.BACK_SPACE && s.length()>0)
                s = s.substring( 0, s.length() - 1 );
            else s += event.getText();

            if( s.length() == 0 ) {
                sm.selectFirst();
                return;
            }
            System.out.println( s );
            for( String item: items ) {
                if( item.startsWith( s ) ) sm.select( item );
            }
        }

    }

    public FilterComboBox( ObservableList< String > items ) {
        super( items );
        this.items = items;

        setOnKeyReleased( new KeyHandler() );
    }
}
于 2012-11-14T02:47:07.127 に答える
3

このようなコードで十分ではないでしょうか?

    comboBox.setOnKeyReleased(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent event) {
            String s = jumpTo(event.getText(), comboBox.getValue(), comboBox.getItems());
            if (s != null) {
                comboBox.setValue(s);
            }
        }
    });

..。

static String jumpTo(String keyPressed, String currentlySelected, List<String> items) {
    String key = keyPressed.toUpperCase();
    if (key.matches("^[A-Z]$")) {
        // Only act on letters so that navigating with cursor keys does not
        // try to jump somewhere.
        boolean letterFound = false;
        boolean foundCurrent = currentlySelected == null;
        for (String s : items) {
            if (s.toUpperCase().startsWith(key)) {
                letterFound = true;
                if (foundCurrent) {
                    return s;
                }
                foundCurrent = s.equals(currentlySelected);
            }
        }
        if (letterFound) {
            return jumpTo(keyPressed, null, items);
        }
    }
    return null;
}

文字を押すと最初の項目にジャンプします。その文字をもう一度押すと、その文字で始まる次の項目にジャンプし、その文字で始まる項目がもうない場合は最初の項目に戻ります。

于 2014-01-12T21:17:00.240 に答える
1

自分のニーズに合ったPerneelのソリューションを実際に入手することはできませんでした。Bhupendraは素晴らしかったですが、1つの詳細がありました:それは最後に一致するアイテムを選択します。0から20までの数値(文字列として)がある場合、「1」を入力すると1ではなく19が返されます。

以下のコードは、これを解決するために必要な行を追加します。

import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.control.ComboBox;
import javafx.scene.control.SingleSelectionModel;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

// TODO: Auto-generated Javadoc
/**
 * The Class FilterComboBox.
 */
public class FilterComboBox extends ComboBox< String > 
{

    /** The items. */
    private ObservableList< String >    items;

    /**
     * The Class KeyHandler.
     */
    private class KeyHandler implements EventHandler< KeyEvent > 
    {

        /** The sm. */
        private SingleSelectionModel< String >  sm;

        /** The s. */
        private String                          s;

        /**
         * Instantiates a new key handler.
         */
        public KeyHandler() 
        {
            sm = getSelectionModel();
            s = "";
        }

        /* (non-Javadoc)
         * @see javafx.event.EventHandler#handle(javafx.event.Event)
         */
        @Override
        public void handle( KeyEvent event ) 
        {
            // handle non alphanumeric keys like backspace, delete etc
            if( event.getCode() == KeyCode.BACK_SPACE && s.length()>0)
            {
                s = s.substring( 0, s.length() - 1 );
            }
            else if(event.getCode() != KeyCode.TAB )
            {
                s += event.getText();
            }

            if( s.length() == 0 ) 
            {
                sm.selectFirst();
                return;
            }
            System.out.println( s );
            for( String item: items ) 
            {
                if( item.startsWith( s ) ) 
                {
                    sm.select( item );
                    return;
                }
            }
        }

    }

    /**
     * Instantiates a new filter combo box.
     *
     * @param items the items
     */
    public FilterComboBox( ObservableList< String > items ) 
    {
        super( items );
        this.items = items;

        setOnKeyReleased( new KeyHandler() );
    }
}

このコンポーネントは、文字列のみを入力として受け取り、文字を入力することでフィルタリングできるComboBoxです。すべてのクレジットはBhupendraにあります。他の人がこの一般的な問題について考えすぎないようにするために、このコードを投稿しただけです。最後の編集:TABが文字と見なされないようにするテストを追加しました(コンポーネントを壊さずにフォーム内を移動できるようにします)

于 2013-12-11T15:06:55.463 に答える
1

これを行う別のオプションがあります。サイトhttps://tech.chitgoks.com
からの例が基礎として採用されました。

これは、必要に応じて前の例でも使用できる洗練されたソリューションを備えています。
入力すると、リストが自動的にスクロールします。とても便利です。

    import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.scene.control.ComboBox;
    import javafx.scene.control.ListView;
    import javafx.scene.input.KeyCode;
    import javafx.scene.input.KeyEvent;

    import java.time.Duration;
    import java.time.Instant;
    import java.util.Collection;

    class SearchComboBox<T> extends ComboBox<T> {

        private static final int IDLE_INTERVAL_MILLIS = 1000;

        private Instant instant = Instant.now();
        private StringBuilder sb = new StringBuilder();

        public SearchComboBox(Collection<T> choices) {
            this(FXCollections.observableArrayList(choices));
        }

        public SearchComboBox(final ObservableList<T> items) {
            this();
            setItems(items);
            getSelectionModel().selectFirst();
        }

        public SearchComboBox() {
            super();

            this.addEventFilter(KeyEvent.KEY_RELEASED, event -> {
                if (event.getCode() == KeyCode.ESCAPE && sb.length() > 0) {
                    resetSearch();
                }
            });

            this.setOnKeyReleased(event -> {

                        if (Duration.between(instant, Instant.now()).toMillis() > IDLE_INTERVAL_MILLIS) {
                            resetSearch();
                        }

                        instant = Instant.now();

                        if (event.getCode() == KeyCode.DOWN || event.getCode() == KeyCode.UP || event.getCode() == KeyCode.TAB) {
                            return;
                        } else if (event.getCode() == KeyCode.BACK_SPACE && sb.length() > 0) {
                            sb.deleteCharAt(sb.length() - 1);
                        } else {
                            sb.append(event.getText().toLowerCase());
                        }

                        if (sb.length() == 0) {
                            return;
                        }

                        boolean found = false;
                        for (int i = 0; i < getItems().size(); i++) {
                            if (event.getCode() != KeyCode.BACK_SPACE && getItems().get(i).toString().toLowerCase().startsWith(sb.toString())) {
                                ListView listView = getListView();
                                listView.getSelectionModel().clearAndSelect(i);
                                scroll();
                                found = true;
                                break;
                            }
                        }

                        if (!found && sb.length() > 0)
                            sb.deleteCharAt(sb.length() - 1);
                    }
            );

            // add a focus listener such that if not in focus, reset the search process
            this.focusedProperty().addListener((observable, oldValue, newValue) -> {
                if (!newValue) {
                    resetSearch();
                } else {
                    scroll();
                }
            });
        }

        private void resetSearch() {
            sb.setLength(0);
            instant = Instant.now();
        }

        private void scroll() {
            ListView listView = getListView();
            int selectedIndex = listView.getSelectionModel().getSelectedIndex();
            listView.scrollTo(selectedIndex == 0 ? selectedIndex : selectedIndex - 1);
        }

        private ListView getListView() {
            return ((ComboBoxListViewSkin) this.getSkin()).getListView();
        }
    }

この例を2つの方法で改善しました。

  1. すべてのコードはクラスにカプセル化されています。外部から接続する必要はありません。
  2. ユーザーがしばらくアクティビティを表示しない場合、検索文字列はリセットされます。別の解決策は、検索をリセットする方法です。Backspaceキーを押すか、ComboBoxのフォーカスを失います。
于 2019-11-15T11:34:12.287 に答える