カスタム列ヘッダーを持つテーブルを作成しようとしています。列ヘッダーに、ユーザーがクリックできるボタンを含めたいです。ボタンの機能は、テーブルから列を削除することです。基本的に、私はこのようなものを構築しようとしています。
これが私のコードです:
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() を呼び出すと、プログラムがクラッシュします。
ということで、よろしければご協力お願いします。多分誰かがこれを行う方法を知っています。たぶん、試してみるために他のアプローチを提案することもできます。または、それがまったく不可能であることを知っているかもしれません...
ご協力ありがとうございました。