8

カスタム列ヘッダーを持つテーブルを作成しようとしています。列ヘッダーに、ユーザーがクリックできるボタンを含めたいです。ボタンの機能は、テーブルから列を削除することです。基本的に、私はこのようなものを構築しようとしています。

これが私のコードです:

public class CustomColumnHeadersTable {

    private static String[] columnNames = {
        "Column 1", "Column 2", "Column 3"
    };
    private static String[][] data = {
        {"A", "B", "C"},
        {"D", "E", "F"},
        {"G", "H", "I"}
    };

    public CustomColumnHeadersTable() {
        DefaultTableModel model = new DefaultTableModel((Object[][]) data, columnNames);
        JTable table = new JTable(model);
        JScrollPane scrollPane = new JScrollPane(table);

        //set Header Renderer of each column to use the Custom renderer
        Enumeration enumeration = table.getColumnModel().getColumns();
        while (enumeration.hasMoreElements()) {
            TableColumn aColumn = (TableColumn) enumeration.nextElement();
            aColumn.setHeaderRenderer(new CustomColumnCellRenderer());
        }

        JFrame frame = new JFrame();
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        frame.setPreferredSize(new Dimension(300, 150));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        CustomColumnHeadersTable ccht = new CustomColumnHeadersTable();
    }
}

class CustomColumnCellRenderer implements TableCellRenderer {

    private static String iconURL = "http://www.accessdubuque.com/images/close_icon.gif";
        //using a URL for the icon, so I don't have to upload the icon with the question
    private static Dimension buttonSize = new Dimension(16, 16);
    private static Dimension buttonBoxSize = new Dimension(16, 16);
    private static Border panelBorder = BorderFactory.createRaisedBevelBorder();

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        JPanel panel = new JPanel();
        JLabel label = new JLabel();
        JButton button = new JButton();
        Box buttonBox = Box.createHorizontalBox();
        BorderLayout layout = new BorderLayout();

        label.setText(table.getColumnName(column));

        try { button.setIcon(new ImageIcon(new URL(iconURL))); }
        catch (MalformedURLException ex) {
            Logger.getLogger(CustomColumnCellRenderer.class.getName()).log(Level.SEVERE, null, ex);
        }

        //set size of the button and it's box
        button.setMaximumSize(buttonSize);
        button.setSize(buttonSize);
        button.setPreferredSize(buttonSize);
        buttonBox.setMaximumSize(buttonBoxSize);
        buttonBox.setSize(buttonBoxSize);
        buttonBox.setPreferredSize(buttonBoxSize);

        button.addMouseListener(new CustomMouseListener()); //doesn't work...

        buttonBox.add(button);
        panel.add(label, BorderLayout.CENTER);
        panel.add(buttonBox, BorderLayout.EAST);

        panel.setBorder(panelBorder);

        return panel;
    }
}

class CustomMouseListener implements MouseListener
{
    public void mouseClicked(MouseEvent e) { System.out.println("Mouse Clicked."); }
    public void mousePressed(MouseEvent e) { System.out.println("Mouse Pressed."); }
    public void mouseReleased(MouseEvent e) { System.out.println("Mouse Released."); }
    public void mouseEntered(MouseEvent e) { System.out.println("Mouse Entered."); }
    public void mouseExited(MouseEvent e) { System.out.println("Mouse Exited."); }
}

デフォルトでは、私の理解が正しければ、JTable は JLabel を使用して列ヘッダーをレンダリングします。私の考えは、カスタムの TableCellRenderer 実装を使用して、いくつかのコンポーネント、つまり JLabel と JButton を含む JPanel から独自の列ヘッダーを作成することです。getTableCellRendererComponent(...) 関数でビルドして返します。

視覚的に、これは機能します。問題は、ボタン (または、それを保持しているパネル) でのマウス クリックを検出できないことです。ボタンに MouseListener を追加するだけでは機能しません。イベントは決してそれに到達しません。

Web 上で同様のものをいくつか見つけましたが、それらは必要な機能を実現していません。

まず、JCheckBox をヘッダーに配置する方法の例を次に示します。

http://java-swing-tips.blogspot.com/2009/02/jtableheader-checkbox.html

これの問題は、ヘッダー全体がチェックボックスになっていることです。チェックボックスまたは関連するラベルをクリックすると、同じ効果が得られます。したがって、列をソートすることはできません。ラベルをクリックすると列がソートされ、閉じるボタンをクリックするとテーブルから列が削除されるようにしたいと思います。つまり、ヘッダーには、別々のマウス イベント ハンドラを持つ 2 つの別々の領域が必要です。

ここで別の例を見つけました:

http://www.devx.com/getHelpOn/10MinuteSolution/20425/1954?pf=true

これには、テーブルのセルへの JButton の配置、テーブル自体でのマウス クリックの検出、クリックが発生した列と行の計算、および適切なボタンへのイベントのディスパッチが含まれます。

これにもいくつか問題があります。まず、ボタンはヘッダーではなくセルにあります。次に、これも 1 つのコンポーネントであり、JPanel 内の複数のコンポーネントではありません。この例からイベントをディスパッチするというアイデアを思いつきましたが、複合コンポーネントに対して機能させることはできません。

別のアプローチを試みました。閉じるボタンの座標を取得できれば、マウス クリックの座標がわかれば、どのボタンがクリックされたかを計算し、イベントを適切にディスパッチできると考えました。いくつかのテストを実行したところ、テーブル ヘッダー内のコンポーネントが実際には画面上に配置されていないことがわかりました。

メイン (パブリック) クラスに静的な JButton 変数を追加し、TableCellRenderer を実装するクラスをメイン クラスの内部クラスにしました。getTableCellRendererComponent(...) では、戻る前に、作成したばかりの JButton をその静的変数に割り当てます。このようにして、いわばそれを処理することができます。次に、メインで、その静的変数を使用して、getX()、getY()、getWidth()、getHeight()、および getLocationOnScreen() を試みました。X、Y、幅、高さはすべて 0 を返します。GetLocationOnScreen() は、この関数が機能するにはコンポーネントが画面上に存在する必要があることを示して、プログラムをクラッシュさせます。

このコードは次のようになります。

    public class CustomColumnHeadersTable {

        private static JButton static_button;
        ///the rest as before....

「class CustomColumnCellRenderer implement TableCellRenderer」は、CustomColumnHeadersTable の内部クラスになります。そのためには、CustomColumnCellRenderer の静的変数を放棄する必要があるため、アイコンや URL などを気にしませんでした。アイコン付きのボタンの代わりに、「BUTTON」と書かれたシンプルなボタンを使用しました...

次に、getTableCellRendererComponent(...) 内で、return ステートメントの直前に、

static_button = button;

そして最後に、main() の中で、これをやってみました:

    System.out.println("X: " + static_button.getX());
    System.out.println("Y: " + static_button.getY());
    System.out.println("W: " + static_button.getWidth());
    System.out.println("H: " + static_button.getHeight());
    System.out.println("LOC: " + static_button.getLocation());
    System.out.println("LOC on screen: " + static_button.getLocationOnScreen());

出力は次のようになります。

X: 0
Y: 0
W: 0
H: 0
LOC: java.awt.Point[x=0,y=0]
Exception in thread "main" java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location
at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:1943)
at java.awt.Component.getLocationOnScreen(Component.java:1917)
...

言い換えれば、ボタンの次元はすべて 0 であり、Java によれば、実際には画面上に配置されていません (私には見えますが...)。getLocationOnScreen() を呼び出すと、プログラムがクラッシュします。

ということで、よろしければご協力お願いします。多分誰かがこれを行う方法を知っています。たぶん、試してみるために他のアプローチを提案することもできます。または、それがまったく不可能であることを知っているかもしれません...

ご協力ありがとうございました。

4

3 に答える 3

2

JButtonはい、 s を aに入れるなど、似たようなことを初めて試みたときは驚きましたJList。あなたが述べた理由でうまくいきません。LayoutManagers をリストに配置するためにリストのようなものを使用したかったことを行う正しい方法JButton

あなたの場合、私はJLabels との組み合わせを使用しようとしますがMouseListeners、これは正しく機能すると思います。をヘッダーとして使用するJLabelと、画像とテキストの両方を提供でき、 に を登録できMouseListenerますJLabelJButton確かに、a をクリックしたとき(つまり、ボタンを押したとき)と同じ視覚効果は得られませんが、同じ機能が可能になります。

注意すべきもう 1 つのことは、 static JButtons (または any JComponent) の使用です。これを行う際の問題は、 の座標がそれ自体にJButton保存されていることです。つまり、別の場所でJButton同じものを使用することはできません。JButtonつまり、同じstatic JButtonものをリストに複数回追加しようとしても、複数JButtonのは表示されずJButton、最後に追加した場所に が表示されるだけです。目的の効果を実現する適切な方法は、 自体ではなく、 の内容(JButtonつまり、画像とテキスト)への静的参照を保持するJButtonことです。次に、新しいインスタンスを作成するだけですJButton画像とテキストを使用して、好きな場所に配置します。一般に、Swing コンポーネントへの静的参照を保持しないことが最善です。これは、同じコンポーネントの複数のインスタンスを持つことができなくなるためです。

スイングは時々それほどスイングしていません。有益な情報を提供できたことを願っています - 良い戦いを続けてください!

于 2011-02-10T19:48:38.187 に答える
1

テーブルの特定の列を「無効にする」オプションがあるJXTable(swingxパレットから)を使用できます。

アンドレイ・イオヌット・アポペイ

于 2012-08-02T13:14:20.783 に答える
0

私は解決策を得ました:

public class CustomColumnHeadersTable {

    public static Vector<JButton> buttons =  new Vector<JButton>();

    private static String[] columnNames = {
            "Column 1", "Column 2", "Column 3"
    };
    private static String[][] data = {
            {"A", "B", "C"},
            {"D", "E", "F"},
            {"G", "H", "I"}
    };

    class ColumnHeaderListener extends MouseAdapter {
        public void mouseClicked(MouseEvent evt) {
            JTable table = ((JTableHeader) evt.getSource()).getTable();
            TableColumnModel colModel = table.getColumnModel();

            int index = colModel.getColumnIndexAtX(evt.getX());
            if (index == -1) {
                return;
            }
            Rectangle headerRect = table.getTableHeader().getHeaderRect(index);

            if( 1== index){
                if(headerRect.contains(evt.getX() , evt.getY())){
                    int xx = evt.getX() - headerRect.x ;

                    for (int i = 0; i < buttons.size(); i++) {
                        JButton  button = buttons.get(i);
                        Rectangle re = button.getBounds();
                        if(re.contains( xx, evt.getY())){
                            System.out.println("Bingle");
                        }

                    }
                }
            }
        }
    }

    public CustomColumnHeadersTable() {
        DefaultTableModel model = new DefaultTableModel((Object[][]) data, columnNames);
        JTable table = new JTable(model);
        JScrollPane scrollPane = new JScrollPane(table);

        JTableHeader header = table.getTableHeader();
        header.addMouseListener(new ColumnHeaderListener());

        //set Header Renderer of each column to use the Custom renderer
        Enumeration enumeration = table.getColumnModel().getColumns();
        while (enumeration.hasMoreElements()) {
            TableColumn aColumn = (TableColumn) enumeration.nextElement();
            aColumn.setHeaderRenderer(new CustomColumnCellRenderer(buttons));
        }

        JFrame frame = new JFrame();
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        frame.setPreferredSize(new Dimension(300, 150));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        CustomColumnHeadersTable ccht = new CustomColumnHeadersTable();
    }
}

class CustomColumnCellRenderer implements TableCellRenderer {

    private static String iconURL = "http://www.accessdubuque.com/images/close_icon.gif";
    //using a URL for the icon, so I don't have to upload the icon with the question
    private static Dimension buttonSize = new Dimension(16, 16);
    private static Dimension buttonBoxSize = new Dimension(16, 16);
    private static Border panelBorder = BorderFactory.createRaisedBevelBorder();

    Vector<JButton> buttons = null;

    JPanel panel = new JPanel();
    JLabel label = new JLabel();
    JButton button = new JButton();

    CustomColumnCellRenderer( Vector<JButton> buttons) {
        this.buttons  = buttons;

        try { button.setIcon(new ImageIcon(new URL(iconURL))); }
        catch (MalformedURLException ex) {
            Logger.getLogger(CustomColumnCellRenderer.class.getName()).log(Level.SEVERE, null, ex);
        }

        //set size of the button and it's box
        button.setMaximumSize(buttonSize);
        button.setSize(buttonSize);
        button.setPreferredSize(buttonSize);

        BorderLayout layout = new BorderLayout();
        panel.setLayout(layout);
        panel.add(label, BorderLayout.CENTER);
        panel.add(button, BorderLayout.EAST);
        panel.setBorder(panelBorder);

        buttons.add(button);
    }

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

        label.setText(table.getColumnName(column));
        return panel;
    }
}
于 2012-08-22T10:25:37.163 に答える