はじめに (対象読者・この記事でわかること)

本記事は、Java デスクトップアプリケーションの UI 改善に関心があるエンジニアを対象としています。特に、Swing または JavaFX で開発中のアプリにステータスバーを設置し、そこに動的なフィルター機能(文字列検索やカテゴリ絞り込み)を組み込みたい方に最適です。この記事を読むことで、ステータスバーにテキスト入力欄とボタンを配置し、入力内容に応じてテーブルやリストの表示内容をリアルタイムで絞り込む実装手順が理解でき、実際に動作するサンプルコードを手元で試すことができるようになります。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。
- Java の基本的な構文と IDE(IntelliJ IDEA や Eclipse など)の使用経験
- Swing または JavaFX の UI コンポーネントに関する基礎知識(JPanelHBoxScene など)
- Maven または Gradle でのプロジェクト構成とビルド方法

ステータスバーにフィルターを組み込む背景と概要

デスクトップアプリでは、一覧表示やログビューなど大量の情報を扱う場面が頻繁にあります。ユーザーが目的のデータをすぐに見つけられるようにするための UI 要素として「フィルター」は不可欠です。従来、フィルターはウィンドウ上部のツールバーやサイドパネルに配置されることが多いですが、画面スペースが限られる場合や、操作の流れをシンプルに保ちたいときには、ステータスバーに組み込む手法が有効です。

ステータスバーは通常、ウィンドウの最下部に配置され、アプリの状態表示やショートカットキーのヒントを提供します。この場所に検索ボックスと「適用」ボタンを追加すれば、ユーザーは現在作業中の画面を離れずに絞り込み操作が可能になります。さらに、フィルターの結果をリアルタイムで反映させることで、操作感が向上し、業務効率が大幅に改善されます。

本節では、Swing と JavaFX の両方で実装できる共通の概念を整理し、以下のポイントに焦点を当てます。

  1. ステータスバーの作成JPanel(Swing)や HBox(JavaFX)を用いたレイアウト構築
  2. フィルター UI の配置JTextFieldJButton(Swing)/TextFieldButton(JavaFX)の組み合わせ
  3. データモデルへのバインディングTableModel(Swing)または ObservableList(JavaFX)に対し、フィルターロジックを適用
  4. リアルタイム更新の実装 – キー入力イベントやボタンアクションで即座に表示内容を再描画

これらを順に実装すれば、汎用的かつ拡張しやすいステータスバー・フィルターが完成します。

Swing での実装手順とサンプルコード

以下では、Swing を使った具体的な実装例を示します。ここで紹介するコードは、Maven プロジェクトの src/main/java/com/example/ui/FilterStatusBarDemo.java に配置できる形です。

ステップ 1 – プロジェクト構成と依存関係の設定

Xml
<!-- pom.xml の抜粋 --> <dependencies> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>21</version> <scope>provided</scope> </dependency> </dependencies>

※ Swing 自体は JDK に標準装備されているため、特別な依存は不要です。上記は、将来的に JavaFX 版とコードを比較したい場合に備えて記載しています。

ステップ 2 – メインフレームとテーブルの作成

Java
public class FilterStatusBarDemo extends JFrame { private JTable table; private DefaultTableModel tableModel; public FilterStatusBarDemo() { setTitle("ステータスバーにフィルターを組み込む例 (Swing)"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(800, 600); setLocationRelativeTo(null); initTable(); initStatusBar(); } private void initTable() { String[] columns = {"ID", "名前", "カテゴリ", "金額"}; Object[][] data = { {1, "商品A", "電子機器", 1200}, {2, "商品B", "文房具", 300}, {3, "商品C", "食品", 500}, // ... 省略可能な大量データ }; tableModel = new DefaultTableModel(data, columns); table = new JTable(tableModel); add(new JScrollPane(table), BorderLayout.CENTER); }

このコードは、簡易的なデータセットを DefaultTableModel に設定し、JTable に表示します。

ステップ 3 – ステータスバーにフィルターパーツを追加

Java
private void initStatusBar() { JPanel statusBar = new JPanel(new BorderLayout()); statusBar.setBorder(BorderFactory.createEtchedBorder()); // フィルターボックス JTextField filterField = new JTextField(); filterField.setToolTipText("名前またはカテゴリで絞り込み"); statusBar.add(filterField, BorderLayout.CENTER); // フィルターボタン JButton applyBtn = new JButton("適用"); statusBar.add(applyBtn, BorderLayout.EAST); // ボタンアクション: フィルター実行 applyBtn.addActionListener(e -> applyFilter(filterField.getText())); // Enter キーでも同様にフィルター filterField.addActionListener(e -> applyFilter(filterField.getText())); add(statusBar, BorderLayout.SOUTH); } private void applyFilter(String query) { TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<>(tableModel); if (query == null || query.trim().isEmpty()) { sorter.setRowFilter(null); } else { RowFilter<DefaultTableModel, Object> rf = RowFilter.regexFilter("(?i)" + Pattern.quote(query)); sorter.setRowFilter(rf); } table.setRowSorter(sorter); }
  • JTextField に入力された文字列を取得し、TableRowSorterRowFilter を組み合わせてテーブル行を動的に絞り込みます。
  • (?i) は正規表現のケースインセンシティブ指定です。
  • applyBtn のクリックと filterField の Enter キーどちらでも同一処理が走ります。

ステップ 4 – アプリ起動エントリポイント

Java
public static void main(String[] args) { SwingUtilities.invokeLater(() -> { new FilterStatusBarDemo().setVisible(true); }); } }

上記コードを実行すると、ウィンドウ下部に検索フィールドと「適用」ボタンが配置されたステータスバーが表示され、テーブルの内容がリアルタイムにフィルタリングされます。

ハマった点やエラー解決

  • RowFilter の正規表現エラー
    入力文字列に特殊文字(*, +, [ など)が含まれると PatternSyntaxException が発生しました。対策として Pattern.quote() でエスケープし、正規表現としての解釈を防止しました。

  • ソートとフィルターの競合
    TableRowSorter を毎回新規作成すると、列ヘッダーのソート状態がリセットされます。解決策として、applyFilter 内で既存の RowSorter を取得し、setRowFilter のみを更新する実装に変更しました。

  • ステータスバーの高さが狭すぎる
    デフォルトの JPanel 高さがテキストフィールドに対して不足していました。setPreferredSize(new Dimension(0, 30)) で適切な高さを指定すると見た目が改善されます。

解決策のまとめ

  1. 正規表現エラーは Pattern.quote() で回避。
  2. ソート状態保持は同一 TableRowSorter インスタンスを使い続ける。
  3. ステータスバーの高さは setPreferredSize で調整。

JavaFX での実装例(簡易版)

JavaFX でも同様のコンセプトが適用可能です。以下は最低限のコード例です。

Java
public class FxFilterStatusBar extends Application { private TableView<Item> tableView = new TableView<>(); private ObservableList<Item> masterData = FXCollections.observableArrayList(); @Override public void start(Stage primaryStage) { initData(); initTable(); TextField filterField = new TextField(); filterField.setPromptText("名前またはカテゴリで絞り込み"); Button applyBtn = new Button("適用"); FilteredList<Item> filtered = new FilteredList<>(masterData, p -> true); applyBtn.setOnAction(e -> filtered.setPredicate(item -> matchesFilter(item, filterField.getText()))); filterField.setOnAction(e -> applyBtn.fire()); SortedList<Item> sorted = new SortedList<>(filtered); sorted.comparatorProperty().bind(tableView.comparatorProperty()); tableView.setItems(sorted); HBox statusBar = new HBox(5, filterField, applyBtn); statusBar.setPadding(new Insets(5)); statusBar.setAlignment(Pos.CENTER_RIGHT); statusBar.setStyle("-fx-border-color: #cccccc; -fx-border-width: 1 0 0 0;"); BorderPane root = new BorderPane(tableView); root.setBottom(statusBar); primaryStage.setScene(new Scene(root, 800, 600)); primaryStage.setTitle("ステータスバーにフィルター (JavaFX)"); primaryStage.show(); } private boolean matchesFilter(Item item, String query) { if (query == null || query.isBlank()) return true; String lower = query.toLowerCase(); return item.getName().toLowerCase().contains(lower) || item.getCategory().toLowerCase().contains(lower); } private void initData() { masterData.addAll( new Item(1, "商品A", "電子機器", 1200), new Item(2, "商品B", "文房具", 300), new Item(3, "商品C", "食品", 500) // ... 必要に応じて追加 ); } private void initTable() { TableColumn<Item, Integer> idCol = new TableColumn<>("ID"); idCol.setCellValueFactory(new PropertyValueFactory<>("id")); TableColumn<Item, String> nameCol = new TableColumn<>("名前"); nameCol.setCellValueFactory(new PropertyValueFactory<>("name")); TableColumn<Item, String> catCol = new TableColumn<>("カテゴリ"); catCol.setCellValueFactory(new PropertyValueFactory<>("category")); TableColumn<Item, Integer> priceCol = new TableColumn<>("金額"); priceCol.setCellValueFactory(new PropertyValueFactory<>("price")); tableView.getColumns().addAll(idCol, nameCol, catCol, priceCol); } public static void main(String[] args) { launch(args); } public static class Item { private final SimpleIntegerProperty id; private final SimpleStringProperty name; private final SimpleStringProperty category; private final SimpleIntegerProperty price; Item(int id, String name, String category, int price) { this.id = new SimpleIntegerProperty(id); this.name = new SimpleStringProperty(name); this.category = new SimpleStringProperty(category); this.price = new SimpleIntegerProperty(price); } public int getId() { return id.get(); } public String getName() { return name.get(); } public String getCategory() { return category.get(); } public int getPrice() { return price.get(); } } }

このサンプルは、FilteredListSortedList を活用し、ステータスバーに配置したテキストフィールドとボタンでテーブルの内容をリアルタイムに絞り込む実装を示しています。Swing 版と同様に、Enter キーで即座にフィルタリングが走ります。

まとめ

本記事では、Java のデスクトップ UI(Swing と JavaFX)において、ステータスバーに検索・絞り込みフィルターを組み込む手順を包括的に解説しました。

  • ステータスバーの位置は画面下部に固定し、ユーザーが常に操作可能な状態を維持。
  • Swing 実装では JTextFieldJButtonJPanel に配置し、TableRowSorterRowFilter でリアルタイムフィルタリングを実現。
  • JavaFX 実装では FilteredListSortedList を用いて同様の機能をシンプルに実装。
  • ハマりポイントは正規表現エスケープ、ソート状態保持、ステータスバーの高さ調整であり、それぞれの解決策を提示しました。

これらを活用すれば、UI の操作性が向上し、ユーザーは大量データの中から目的情報を瞬時に抽出できるようになります。今後は、複数条件の組み合わせフィルターや、非同期検索(バックエンド API との連携)といった発展的なトピックにも挑戦してみてください。

参考資料