JavaFX の TextField に入力補完を付けてみる

この記事は JavaFX Advent Calendar 2016 の 20 日目です。昨日はひらおかゆみ (@yumix_h) の「JavaFXで小惑星を描いてみよう」です。

2~3 年前、JavaFX のフロントエンドを持つアプリケーションのテキスト入力欄に、Google 検索のような入力補完 (履歴参照) 機能を追加する要望が持ち上がって、会社の後輩と 2 人で対応したことがあります。後輩の退職後に機能のメンテナンスを重ねるうちに、アプリケーションから独立した部品として使用できるようになりました。

実際のアプリケーションで使用しているソースコードは使用できませんが、今回は機能設計から新たに書き起こしたものをご紹介します (Ethereal と Wireshark の関係と同じ位置づけだと認識しています)。

はじめに、入力補完 (履歴参照) 機能の実行サンプルを figure 1 に示します。

javafx-textfield-history.png
figure 1 - 入力補完 (履歴参照) 機能のサンプル

この機能を実装するのに必要なコンポーネントは、以下の 2 つです。

いずれも Snapshot ですが私の Nexus https://repo1.haswell.jp/ にも載せてあります。

  • groupId: jp.coppermine.tools
  • artifactId: coppermine-tools-javafx-history
  • version: 現時点ではともに 0.2.0

前者はメモリやファイルなどをストアとして利用する履歴参照機能を提供するモジュールです。今回はそのうちファイルをストアとするものを使用しました。また、後者は前者を利用した JavaFX のコントロールもどきで、実態は TextField の入力内容と連動する ListView です。前者は私のオリジナルです。後者は後輩がアルゴリズムと初期実装を担当し、私が Lambda と Stream API による再実装とアプリケーション本体からの分離を担当しました。

使い方

詳しくは https://github.com/khasunuma/javafx-history-sample を参照して頂きたいのですが、大雑把には次のようになります。

まず、履歴参照機能を取り付ける TextField と紐付くコントローラー・クラスに FileHistoryOperation インタフェースを実装します。ほとんどの機能はメソッドのデフォルト実装にて提供しています。ただし、getHistory メソッドだけは純粋な抽象メソッドであるため以下のように実装する必要があります。

private History history = new FileHistory(getPath());

@Override
public History getHistory() {
    return history;
}

続いて、HistoryView クラスのインスタンスを生成します。

private HistoryView historyView = new HistoryView();

コントローラー・クラスの initialize メソッドに以下の記述を追加します。

initializeHistory();
loadKeywords();
historyView.attach(textField, () -> getKeywords());

最後にトリガーとなるボタンやキーイベントを適切に実装します。これで、最初のスクリーンショットのような入力補完とコマンド履歴が使えるようになります。

当初、私は過去の入力履歴を保存して編集可能なコンボボックスで選択できるような実装で十分だと考えていて、後輩にもそう伝えていたのですが、彼はあくまで Google 検索のような入力補完にこだわり、2 人で試行錯誤を重ねて最初の実装を実現しました。今回はオリジナルをリバースエンジニアリングしたものであり、Java SE 8 で追加された機能を用いて実装の大部分を書き換えたため、最初の実装からは大きく姿を変えています。しかし、入力補完のアルゴリズムは後輩が考案したものを基本的に踏襲しており、私の修正は複雑なループを Stream API に置き換えたことだけでした。現在、後輩は遠い異国の地にいますが、そこでソフトウェア技術者として大いに活躍していることと思います。

明日は深井さん (@fukai_yas) です。