我正在尝试使用自定义列标题创建一个表。我希望列标题包含一个用户可以单击的按钮。按钮的功能是从表中删除列。从本质上讲,我正在尝试构建类似this的内容。
这是我的代码:
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添加到按钮不起作用。事件永远不会到达。
我在网上发现了几个类似的东西,但它们没有达到我需要的功能。
首先,有一个如何将JCheckBox放入标题的例子:
http://java-swing-tips.blogspot.com/2009/02/jtableheader-checkbox.html
这个问题是整个标题是复选框。单击复选框或关联的标签会产生相同的效果。因此,无法对列进行排序。我想这样做,以便点击标签对列进行排序,然后单击关闭按钮将从表中删除列。换句话说,标题需要有两个独立的区域和单独的鼠标事件处理程序。
我在这里找到了另一个例子:
http://www.devx.com/getHelpOn/10MinuteSolution/20425/1954?pf=true
这涉及将JButtons放入表格的单元格中,然后检测表格本身的鼠标点击,计算发生点击的列和行,并将事件分派给相应的按钮。
这也有几个问题。首先,按钮位于单元格中,而不是标题中。其次,这只是一个组件,而不是JPanel中的几个组件。虽然我从这个例子中得到了调度事件的想法,但我无法使其适用于复合组件。
我尝试了另一种方法。我推断,如果我可以获得关闭按钮的坐标,那么知道鼠标点击的坐标我可以计算点击了哪个按钮,并适当地调度事件。我运行了几个测试,发现表头中的组件实际上并不位于屏幕上。
我在main(public)类中添加了一个静态JButton变量,并使实现TableCellRenderer的类成为主类的内部类。在getTableCellRendererComponent(...)中,在返回之前,我将刚刚创建的JButton分配给该静态变量。这样,我就能掌握它,可以这么说。然后,在main中,我尝试使用该静态变量获取X(),getY(),getWidth(),getHeight()和getLocationOnScreen()。 X,Y,Width和Height都返回0。 GetLocationOnScreen()使程序崩溃,说明该组件必须出现在屏幕上才能使用此功能。
这个代码看起来像这样:
public class CustomColumnHeadersTable {
private static JButton static_button;
///the rest as before....
“类CustomColumnCellRenderer实现TableCellRenderer”成为CustomColumnHeadersTable的内部类。为此,我必须放弃CustomColumnCellRenderer中的静态变量,所以我没有打扰图标或网址或类似的东西。我没有使用带有图标的按钮,只使用了一个简单的按钮,上面写着“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()会使程序崩溃。
所以,如果可以,请帮忙。也许有人知道如何做到这一点。也许你可以建议一些其他尝试方法。或者,也许你知道它根本不可能......
感谢您的帮助。
答案 0 :(得分:2)
是的,我第一次尝试做类似的事情时感到很惊讶,比如把JButton
放到JList
中 - 由于你说的原因,它并不是很有效。正确的方法来执行我想要使用类似列表的LayoutManager
将JButton
放入列表中的方法。
在您的情况下,我会尝试使用JLabel
和MouseListeners
的组合,我相信它们会正常运行。使用JLabel
作为标题可以同时提供图片和文字,您可以在MouseListener
上注册JLabel
。当然,你不会得到与点击JButton
时相同的视觉效果(即按钮按下),但它将允许相同的功能。
需要注意的另一件事是使用静态JButton
(或任何JComponent
)。这样做的问题是JButton
的坐标存储在JButton
本身,这意味着您不能在不同的地方使用相同的JButton
。换句话说,如果您尝试多次将相同的static JButton
添加到列表中,则不会看到多个JButton
,您只会在您所在的位置看到JButton
最后添加了它。实现所需效果的正确方法是保留对JButton
的内容的静态引用 - 即图像和文本 - 而不是JButton
本身。然后,使用图像和文本实例化一个新的JButton
,并将其放在任何您想要的位置。一般情况下,最好不要将静态引用保留在Swing组件中,因为它不可能有同一组件的多个实例。
有时摇摆不是那么摇摆。希望我提供了一些有用的信息 - 继续争取好斗!
答案 1 :(得分:1)
您可以使用具有该选项的JXTable(来自swingx调色板)来“禁用”表格中的特定列。
Andrei Ionut Apopei
答案 2 :(得分: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;
}
}