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

2025-08-06

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

JTable(テーブル表示)

JTableの例
JTableを使ったデータ表表示の例

基本的なJTableの実装

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);
        });
    }
}

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

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の実装

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

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の実装

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

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;
    }
}

実践例:JTableと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アプリケーションのアーキテクチャ」では、これらのコンポーネントを効果的に組み合わせる設計パターンを解説します。