プラグイン作って覚えたことを淡々と記録するよ(TableView編)

プラグインSDKにTableViewってコンポーネントがあったから使ってみたよ(com.intellij.ui.tableパッケージに入ってる)。曰く,

Provides a useful and easily configurable wrapper around the Swing JTable component.

なんだそうな。


そもそも,JTableの使い方自体よくわかってないので,どんなに便利になったのかは,いまいちわかってないんだけど,まあいいや。これを使う上でのポイントは,ListTableModelちゅう専用のTableModelにあるっぽい。説明が面倒臭いので,コード載っけとく。


SomeBeans:テーブルに表示したいJavaBeans(テキトーなのでいい)。

public class SomeBeans {
    private String firstname;
    private String lastname;
    private int age;
    private String email;

    public SomeBeans(int seed) {
        firstname = "aaaa:" + seed;
        lastname = "bbbb";
        age = 99 * seed;
        email = "foo@bar.boo";
    }
    :
}


TestTableView:TableViewを貼っ付けたFormのコード。

public class TestTableView {
    private JPanel mainPanel;
    // TableViewをSomeBeansでパラメタ化しとく
    private TableView<SomeBeans> table;

    public TestTableView() {
        table.setModel(createListTableModel());
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // 何でか知らんが,getTableHeader()しないとヘッダが表示されなかった
        JTableHeader header = table.getTableHeader();

        header.setReorderingAllowed(false);
    }

    private ListTableModel<SomeBeans> createListTableModel() {
        // ListTableModelはTableViewと同じクラス(SomeBeans)でパラメタ化しとく
        ListTableModel<SomeBeans> model = new ListTableModel<SomeBeans>(
                // ColumnInfoは,行の型と列の型の2つでパラメタ化する
                new ColumnInfo<SomeBeans, String>("First Name") {
                    //                    コンストラクタ引数がヘッダ名になる
                    // 必ず実装するのは,valueOf()
                    public String valueOf(SomeBeans item) {
                        // このカラムの値を返す
                        return item.getFirstname();
                    }

                    // カラム幅の初期値
                    //   TableColumn.setPreferredWith()に相当してるっぽい
                    public int getAdditionalWidth() { return 100; }
                },
                            :
                new ColumnInfo<SomeBeans, Integer>("Age") {
                    public Integer valueOf(SomeBeans item) {
                        return item.getAge();
                    }

                    // getAdditionalWidth()と違って,getWidth()だとカラム幅は固定される
                    //   TableColumn.setMaxWidth()に相当してるっぽい
                    public int getWidth(JTable table) { return 30; }

                    // Comparatorを返すカラムは自動的にソート可能になる
                    public Comparator<SomeBeans> getComparator() {
                        return new Comparator<SomeBeans>() {
                            public int compare(SomeBeans o1, SomeBeans o2) {
                                return o2.getAge() - o1.getAge();
                            }
                        };
                    }
                },
                            :
        );

        // これが実データ。当然,同じ型(SomeBeans)でパラメタ化しとく
        List<SomeBeans> list = new ArrayList<SomeBeans>();
        for (int i = 0; i < 10; i++) list.add(new SomeBeans(i));

        // 実データをListTableModelにセットする
        model.setItems(list);

        return model;
    }
    :
}


ColumnInfoの組み方次第にもよるが,素のJTable,DefaultTableModelよりは組みやすそうかも。少なくともカラムのソートの実装は簡単そうだ。じゃあ,早速Maven Repo SearchプラグインをTableView化するかと言えば,興が乗らないのでまた今度。:-P
http://terai.xrea.jp/Swing/TableSorter.html


Java6だとソートも簡単みたいだけど,OSXがJava5なんで,IDEAプラグインはJava5でビルドが主流なのよね...。
http://terai.xrea.jp/Swing/TableRowSorter.html


おまけ:GUIのテストするだけだったら,いちいちプラグインとして実行させる必要はなく,Formのバインドコードにこんな感じのメインルーチン書いとけばOK。

public static void main(String[] args) throws Exception {
    JFrame frame = new JFrame();
    UIManager.setLookAndFeel(new WindowsLookAndFeel());
    SwingUtilities.updateComponentTreeUI(frame);
        
    frame.setContentPane(new TestTableView().mainPanel);
    frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    });
    frame.pack();
    frame.setVisible(true);
}


ちなみに,先のTestTableViewを実行すると,こんな感じになる。


(追記)Maven Repo SearchをTableView化してみた。気付いた点が2つほどあるので,メモっておく。


カラム幅について:ColumnInfo.getAdditionalWidth()やgetWidth(JTable table)で決まるって言ったのは間違い(TableView.setSize()に答えが書いてあるので,詳しくはそちらを参照)。
ColumnInfo.getPreferredStringValue()もしくは,getMaxStringValue()でカラム幅の元ネタとなる文字列を返すのが本筋のようだ。
なんで,たとえば5文字分のカラム幅が欲しいのであれば,こんなコードを書くと良い。

public class SomeColumnInfo extends ColumnInfo<SomeBeans, String> {
    :
    public String getPreferredStringValue() {
        return "12345";
    }


ヘッダクリックによるソート:TableViewにComparetorを返すColumnInfoをセットすることで,該当カラムがソート可能になる。それは,まあ便利なんだけど,なんでかソートしてもヘッダ項目の再描画がされず,ソート項目のハイライトやアローが以前のままになる(データ部分はちゃんとソートされるよ)。で,ウインドウを動かすとかカラム幅を変更するとか再描画を促すと,最新の状態になる。
しょうがないんで,TableView.onHeaderClicked(int)を上書きして,こんなことしてんだけど,ちょっと腑に落ちないよなぁ。:-(

public class SomeTableView extends TableView<SomeBeans> {
    :
    protected void onHeaderClicked(int column) {
        super.onHeaderClicked(column);
        getTableHeader().repaint();     // ヘッダを再描画
    }


とは言え,JTableよりは便利なのは確かなんで,プラグインで表を使う場合はTableViewを積極的に使っていこう。