Skip to content
Code Blue プログラミング学習ノート
Java/Swing

より高度なコンポーネント(JTable, JList, JComboBoxなど)

Swingには基本的なコンポーネントに加え、より複雑なデータ表示や操作が可能な高度なコンポーネントが用意されています。これらのコンポーネントを活用することで、本格的なアプリケーションのインターフェースを構築できます。

JTable(テーブル表示)

JTableは、データを表形式で表示するためのSwingコンポーネントです。行と列で構成されており、Excelの表のように複数のデータを一覧で表示できます。

例えば、社員一覧や商品一覧、成績表など、多くの情報を整理して表示したい場合によく利用されます。JTable自体はデータを保持しておらず、通常はTableModelと呼ばれるデータモデルから表示する内容を取得します。

また、セルの選択や編集、スクロール表示にも対応しており、実用的なデスクトップアプリケーションで広く利用されています。

基本的なJTableの実装

以下のコードを用いて、JTableを使用してデータを表形式で表示する基本的な方法を学びます。

最初に表示するデータと列名を用意し、それらをDefaultTableModelに設定してテーブルのデータモデルを作成しています。さらに、isCellEditable()をオーバーライドしてfalseを返すことで、セルを編集できない読み取り専用のテーブルにしています。

作成したモデルをJTableへ渡して表示し、JScrollPaneで囲むことで、データが多くなった場合でもスクロールできるようにしています。JTableは一覧画面を作成する際によく利用されるSwingコンポーネントです。

import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class SimpleTableExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("JTable基本例");
            frame.setSize(600, 400);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            // テーブルデータの作成
            Object[][] data = {
                {"田中太郎", 25, "東京"},
                {"山田花子", 30, "大阪"},
                {"佐藤健二", 28, "福岡"}
            };

            // カラム名
            String[] columns = {"名前", "年齢", "住所"};

            // テーブルモデルの作成
            DefaultTableModel model = new DefaultTableModel(data, columns) {
                @Override
                public boolean isCellEditable(int row, int column) {
                    return false; // セル編集不可
                }
            };

            // JTableの作成
            JTable table = new JTable(model);
            JScrollPane scrollPane = new JScrollPane(table);

            frame.add(scrollPane);
            frame.setVisible(true);
        });
    }
}

カスタムテーブルモデルの実装

以下のコードを用いて、AbstractTableModelを継承して独自のテーブルモデルを作成する方法を学びます。

商品名や価格、在庫数を持つProductオブジェクトの一覧をテーブルへ表示するために、行数や列数、列名、各セルに表示する値を取得するメソッドをオーバーライドしています。JTableはテーブルモデルからデータを取得して画面へ表示するため、表示するデータの構造を柔軟に定義できます。

import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import java.util.List;

public class CustomTableModelExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("カスタムテーブルモデル例");
            frame.setSize(600, 400);

            // カスタムモデルの作成
            ProductTableModel model = new ProductTableModel(List.of(
                new Product("ノートPC", 120000, 10),
                new Product("スマートフォン", 80000, 15),
                new Product("タブレット", 50000, 8)
            ));

            JTable table = new JTable(model);
            JScrollPane scrollPane = new JScrollPane(table);

            frame.add(scrollPane);
            frame.setVisible(true);
        });
    }
}

class ProductTableModel extends AbstractTableModel {
    private final List<Product> products;
    private final String[] columns = {"商品名", "価格", "在庫数"};

    public ProductTableModel(List<Product> products) {
        this.products = products;
    }

    @Override
    public int getRowCount() {
        return products.size();
    }

    @Override
    public int getColumnCount() {
        return columns.length;
    }

    @Override
    public String getColumnName(int column) {
        return columns[column];
    }

    @Override
    public Object getValueAt(int row, int column) {
        Product product = products.get(row);
        return switch (column) {
            case 0 -> product.name();
            case 1 -> product.price();
            case 2 -> product.stock();
            default -> null;
        };
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return switch (columnIndex) {
            case 0 -> String.class;
            case 1 -> Integer.class;
            case 2 -> Integer.class;
            default -> Object.class;
        };
    }
}

record Product(String name, int price, int stock) {}

実際の業務では、データベースやオブジェクトの情報を一覧表示することが多く、カスタムテーブルモデルは実用的なアプリケーションでよく利用されます。

JList(リスト表示)

基本的なJListの実装

このプログラムでは、JListを使用して複数の項目を一覧表示する方法を学びます。文字列の配列をJListに渡すことで、プログラミング言語の一覧を画面に表示しています。

setSelectionMode()では複数の項目を選択できるように設定し、ListSelectionListenerを登録することで、選択内容が変更されたときに処理を実行しています。この例では、選択された項目をコンソールへ出力しています。また、JScrollPaneでJListを囲むことで、表示しきれない項目がある場合でもスクロールして閲覧できるようになっています。

import javax.swing.*;

public class SimpleListExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("JList基本例");
            frame.setSize(300, 400);

            // リストデータ
            String[] data = {
                "Java", "Python", "C++", "JavaScript", "Ruby",
                "Go", "Kotlin", "Swift", "Rust", "TypeScript"
            };

            // JListの作成
            JList<String> list = new JList<>(data);
            list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

            // 選択イベントのリスナー
            list.addListSelectionListener(e -> {
                if (!e.getValueIsAdjusting()) {
                    System.out.println("選択された項目: " + list.getSelectedValue());
                }
            });

            JScrollPane scrollPane = new JScrollPane(list);
            frame.add(scrollPane);
            frame.setVisible(true);
        });
    }
}

カスタムレンダリング付きJList

このプログラムでは、JListの表示内容を自由に変更するカスタムレンダラーの作成方法を学びます。

通常のJListは文字列だけを表示しますが、この例ではLanguageオブジェクトを一覧表示し、プログラミング言語名とリリース年、JVM対応の有無を1つの項目として表示しています。表示方法はListCellRendererを実装したLanguageCellRendererで定義しており、選択された項目は背景色や文字色を変更して見やすくしています。

import javax.swing.*;
import java.awt.*;

public class CustomListRendererExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("カスタムレンダリングJList");
            frame.setSize(350, 400);

            // 複雑なデータのリスト
            Language[] languages = {
                new Language("Java", 1995, true),
                new Language("Python", 1991, true),
                new Language("C++", 1985, false),
                new Language("Kotlin", 2011, true)
            };

            JList<Language> list = new JList<>(languages);
            list.setCellRenderer(new LanguageCellRenderer());

            frame.add(new JScrollPane(list));
            frame.setVisible(true);
        });
    }
}

class Language {
    String name;
    int year;
    boolean jvmBased;

    public Language(String name, int year, boolean jvmBased) {
        this.name = name;
        this.year = year;
        this.jvmBased = jvmBased;
    }
}

class LanguageCellRenderer extends JPanel implements ListCellRenderer<Language> {
    private JLabel nameLabel = new JLabel();
    private JLabel detailLabel = new JLabel();

    public LanguageCellRenderer() {
        setLayout(new BorderLayout(5, 5));
        add(nameLabel, BorderLayout.WEST);
        add(detailLabel, BorderLayout.CENTER);
        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    }

    @Override
    public Component getListCellRendererComponent(
            JList<? extends Language> list, Language value, 
            int index, boolean isSelected, boolean cellHasFocus) {

        nameLabel.setText(value.name);
        detailLabel.setText("リリース年: " + value.year + 
                          (value.jvmBased ? " (JVM)" : ""));

        if (isSelected) {
            setBackground(list.getSelectionBackground());
            setForeground(list.getSelectionForeground());
        } else {
            setBackground(list.getBackground());
            setForeground(list.getForeground());
        }

        return this;
    }
}

このようにカスタムレンダラーを利用することで、オブジェクトの情報を分かりやすく表示できる実用的なリストを作成できます。

JComboBox(ドロップダウンリスト)

基本的なJComboBoxの実装

このプログラムでは、JComboBoxを使用して選択式のリストを作成する方法を学びます。

国名の配列をJComboBoxに設定することで、ドロップダウンリストから項目を選択できるようにしています。setSelectedIndex(-1)を指定することで、起動時は何も選択されていない状態になります。

また、ItemListenerを登録することで選択内容の変更を検知し、選択された国名をラベルへ表示しています。JComboBoxは、複数の選択肢の中から1つを選択させたい場合によく利用されるSwingコンポーネントです。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;

public class ComboBoxExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("JComboBox例");
            frame.setSize(300, 200);
            frame.setLayout(new FlowLayout());

            String[] countries = {
                "日本", "アメリカ", "イギリス", 
                "フランス", "ドイツ", "中国"
            };

            JComboBox<String> comboBox = new JComboBox<>(countries);
            comboBox.setSelectedIndex(-1); // 初期選択なし

            JLabel resultLabel = new JLabel("選択された国: ");

            // 選択変更イベント
            comboBox.addItemListener(e -> {
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    resultLabel.setText("選択された国: " + comboBox.getSelectedItem());
                }
            });

            frame.add(comboBox);
            frame.add(resultLabel);
            frame.setVisible(true);
        });
    }
}

カスタムオブジェクトを使用したJComboBox

このプログラムでは、JComboBoxに独自のオブジェクトを表示する方法を学びます。

Personクラスのオブジェクトをコンボボックスへ追加し、PersonCellRendererを使用して名前と年齢が表示されるようにカスタマイズしています。また、項目が選択されるとActionListenerが実行され、選択されたPersonオブジェクトを取得してラベルに詳細情報を表示しています。

import javax.swing.*;
import java.awt.*;

public class CustomObjectComboBox {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("カスタムオブジェクトComboBox");
            frame.setSize(400, 200);

            // カスタムオブジェクトの配列
            Person[] people = {
                new Person("田中太郎", 25),
                new Person("山田花子", 30),
                new Person("佐藤健二", 28)
            };

            // ComboBoxの作成
            JComboBox<Person> comboBox = new JComboBox<>(people);
            comboBox.setRenderer(new PersonCellRenderer());

            JLabel infoLabel = new JLabel(" ");

            comboBox.addActionListener(e -> {
                Person selected = (Person)comboBox.getSelectedItem();
                if (selected != null) {
                    infoLabel.setText(selected.name + " (" + selected.age + "歳)");
                }
            });

            frame.setLayout(new FlowLayout());
            frame.add(comboBox);
            frame.add(infoLabel);
            frame.setVisible(true);
        });
    }
}

class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name;
    }
}

class PersonCellRenderer extends DefaultListCellRenderer {
    @Override
    public Component getListCellRendererComponent(
            JList<?> list, Object value, int index, 
            boolean isSelected, boolean cellHasFocus) {

        super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

        if (value instanceof Person person) {
            setText(person.name + " (" + person.age + "歳)");
        }

        return this;
    }
}

このようにJComboBoxは文字列だけでなくオブジェクトも扱うことができ、表示内容を自由に変更できるため、実際の業務アプリケーションでもよく利用されるコンポーネントです。

実践例:JTableとJComboBoxの連携

このプログラムでは、JTableとJComboBoxを組み合わせて、テーブル内のデータを選択式で編集する方法を学びます。プロジェクト名とステータスを表形式で表示し、ステータス列だけを編集できるように設定しています。

さらに、ステータス列にはJComboBoxをセルエディタとして設定しているため、「未着手」や「進行中」などの候補から選択して値を変更できます。また、テーブルの行が選択されると、その行のプロジェクト名とステータスを取得してコンソールへ表示しています。

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;

public class TableComboIntegration {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("テーブルとコンボボックスの連携");
            frame.setSize(600, 400);

            // テーブルデータ
            Object[][] data = {
                {"プロジェクトA", "未着手"},
                {"プロジェクトB", "進行中"},
                {"プロジェクトC", "完了"}
            };

            // カラム名
            String[] columns = {"プロジェクト名", "ステータス"};

            // テーブルモデル
            DefaultTableModel model = new DefaultTableModel(data, columns) {
                @Override
                public boolean isCellEditable(int row, int column) {
                    return column == 1; // ステータス列のみ編集可能
                }
            };

            // JTableの作成
            JTable table = new JTable(model);

            // ステータス列にコンボボックスを設定
            String[] statuses = {"未着手", "進行中", "保留", "完了"};
            JComboBox<String> comboBox = new JComboBox<>(statuses);
            table.getColumnModel().getColumn(1).setCellEditor(new DefaultCellEditor(comboBox));

            // テーブルの選択変更イベント
            table.getSelectionModel().addListSelectionListener(e -> {
                if (!e.getValueIsAdjusting()) {
                    int row = table.getSelectedRow();
                    if (row >= 0) {
                        System.out.println("選択行: " + row + 
                                       ", プロジェクト: " + table.getValueAt(row, 0) + 
                                       ", ステータス: " + table.getValueAt(row, 1));
                    }
                }
            });

            frame.add(new JScrollPane(table));
            frame.setVisible(true);
        });
    }
}

このような機能は、業務システムの一覧画面などでよく利用されます。

高度なコンポーネント使用時のベストプラクティス

  1. モデルとビューの分離 – TableModelやListModelを使用してデータと表示を分離
  2. レンダラーのカスタマイズ – セルレンダラーを活用して見た目を最適化
  3. 大規模データ対応 – 大量データには遅延ロードを検討
  4. イベント処理の最適化 – 必要なイベントのみリスナー登録
  5. スレッドセーフティ – データ更新はEDTで行う
// モデルとビューの分離例(JTableの場合)
DefaultTableModel model = new DefaultTableModel(data, columns);
JTable table = new JTable(model);

// データ更新はモデルに対して行う
SwingUtilities.invokeLater(() -> {
    model.addRow(new Object[]{"新しいデータ", 123});
    model.fireTableDataChanged(); // 必要に応じて通知
});

これらの高度なコンポーネントを組み合わせることで、データ集約型の本格的なアプリケーションインターフェースを構築できます。次に学ぶ「Swingアプリケーションのアーキテクチャ」では、これらのコンポーネントを効果的に組み合わせる設計パターンを解説します。