JTable不支持显示每个列的聚合数据的页脚。受Oracle/Suns bug database建议的解决方案的启发,看起来很有前途,我开始采用滚动窗格边框绘制页脚的方法。
现在起初我只想要一个'概念证明',看起来我几乎就在那里,每列都有一个页脚,宽度是同步的。所以它似乎工作得很好,除了一件事,没有文字画!我希望从每个“页脚单元格”上绘制的getFooterValueAt
返回的虚拟值,但它们都是空白的,只有背景(仅用于测试)被绘制。
为什么没有画任何文字?我在paintFooter
中的位置/尺寸计算有问题吗?
import static java.awt.BorderLayout.CENTER;
import static java.awt.Color.BLUE;
import static java.awt.Color.CYAN;
import static java.awt.Color.GREEN;
import static java.awt.Color.LIGHT_GRAY;
import static java.awt.Color.MAGENTA;
import static java.awt.Color.ORANGE;
import static java.awt.Color.PINK;
import static java.awt.Color.RED;
import static java.awt.Color.WHITE;
import static java.awt.Color.YELLOW;
import static javax.swing.JTable.AUTO_RESIZE_OFF;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.CellRendererPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
/**
* Demo application for JTable with footer.
*
* @author Martin Uhlén
*/
public class TableFooterBorderDemo extends JFrame
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
new TableFooterBorderDemo().setVisible(true);
}
});
}
private TableFooterBorderDemo()
{
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLayout(new BorderLayout());
JTable table = createTable();
JScrollPane scroll = new JScrollPane(table);
add(scroll, CENTER);
TableFooter.install(scroll, table);
pack();
positionAtMiddleOfScreen();
}
private void positionAtMiddleOfScreen()
{
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setLocation((int) ((screenSize.getWidth() / 2) - (getWidth() / 2)),
(int) ((screenSize.getHeight() / 2) - (getHeight() / 2)));
}
private JTable createTable()
{
Object[][] data = new Object[][]
{
{ "A", "*", "*", "*", "*", "*" },
{ "*", "B", "*", "*", "*", "*" },
{ "*", "*", "C", "*", "*", "*" },
{ "*", "*", "*", "D", "*", "*" },
{ "*", "*", "*", "*", "E", "*" },
{ "*", "*", "*", "*", "*", "F" }
};
Object[] columns = new Object[] { "A", "B", "C", "D", "E", "F" };
DefaultTableModel model = new DefaultTableModel(data, columns);
JTable table = new JTable(model);
table.setAutoResizeMode(AUTO_RESIZE_OFF);
return table;
}
/**
* A Border for JScrollPane that paints a footer for JTable.
*/
private static class TableFooter implements Border
{
private static final Color[] COLORS = {RED, GREEN, BLUE, YELLOW, PINK, CYAN, LIGHT_GRAY, MAGENTA, ORANGE, WHITE};
private final JScrollPane scroll;
private final JTable table;
private final CellRendererPane cellRendererPane;
TableFooter(JScrollPane scroll, JTable table)
{
this.scroll = scroll;
this.table = table;
cellRendererPane = new CellRendererPane();
}
public static TableFooter install(JScrollPane scroll, JTable table)
{
verify(scroll, table);
TableFooter footer = new TableFooter(scroll, table);
RepaintListener repainter = new RepaintListener(scroll);
scroll.getViewport().addChangeListener(repainter);
scroll.getHorizontalScrollBar().addAdjustmentListener(repainter);
table.getColumnModel().addColumnModelListener(repainter);
scroll.setViewportBorder(footer);
return footer;
}
private static void verify(JScrollPane scroll, JTable table)
{
if (scroll.getViewport().getView() != table)
{
throw new IllegalArgumentException("Given table must be inside given scroll pane");
}
}
/**
* @see javax.swing.border.Border#paintBorder(java.awt.Component, java.awt.Graphics, int, int, int, int)
*/
@Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
{
System.out.println("x: " + x + ", y: " + y + ", width: " + width + ", height: " + height);
System.out.println("viewRect: " + scroll.getViewport().getViewRect());
paintFooter(g, x, y, width, height);
}
private void paintFooter(Graphics g, int x, int y, int width, int height)
{
Color oldColor = null;
Component cellRendererComponent = null;
int columnWidths = x - scroll.getViewport().getViewRect().x;
for (int column = 0; column < table.getColumnCount(); column++)
{
TableCellRenderer cellRenderer = table.getCellRenderer(0, column);
cellRendererComponent = cellRenderer.getTableCellRendererComponent(table, getFooterValueAt(column), false, false, 0, column);
if (oldColor == null)
{
oldColor = cellRendererComponent.getBackground();
}
int columnWidth = table.getColumnModel().getColumn(column).getWidth();
cellRendererComponent.setBackground(COLORS[column % COLORS.length]);
cellRendererPane.paintComponent(g, cellRendererComponent, scroll, columnWidths, y, columnWidth, height);
columnWidths += columnWidth;
}
if (cellRendererComponent != null)
{
cellRendererComponent.setBackground(oldColor);
}
}
private Object getFooterValueAt(int viewColumn)
{
return "Column " + viewColumn;
}
@Override
public Insets getBorderInsets(Component c)
{
return new Insets(0, 0, table.getRowHeight(), 0);
}
@Override
public boolean isBorderOpaque()
{
return true;
}
}
/**
* Repaints JScrollPane when needed.
*/
private static class RepaintListener implements ChangeListener, AdjustmentListener, TableColumnModelListener
{
private final JScrollPane scroll;
RepaintListener(JScrollPane scroll)
{
this.scroll = scroll;
}
@Override
public void columnAdded(TableColumnModelEvent e)
{
repaint();
}
@Override
public void columnRemoved(TableColumnModelEvent e)
{
repaint();
}
@Override
public void columnMoved(TableColumnModelEvent e)
{
repaint();
}
@Override
public void columnMarginChanged(ChangeEvent e)
{
repaint();
}
@Override
public void columnSelectionChanged(ListSelectionEvent e)
{
repaint();
}
@Override
public void adjustmentValueChanged(AdjustmentEvent e)
{
repaint();
}
@Override
public void stateChanged(ChangeEvent e)
{
repaint();
}
private void repaint()
{
scroll.repaint();
}
}
}
答案 0 :(得分:4)
将JComponent/JTextComponents
放置到JVievport
(有利于MouseEvents
)
将JComponent/JTextComponents
放置到Glasspane
(有利于MouseEvents
)
将JComponent/JTextComponents
放置到JPanel
,然后将JPanel
放到JScrollPane
,通过工具ComponentListener
,您可以将JPanel
调整为JViewport's Dimension
仅JViewport's Dimension
需要将较小的JPa大小调整为Borders
在所有情况下都需要将自己的JScrollPane
添加到import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableFilterRow extends JFrame implements TableColumnModelListener {
private static final long serialVersionUID = 1L;
private JTable table;
private JPanel filterRow;
public TableFilterRow() {
table = new JTable(3, 5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane);
table.getColumnModel().addColumnModelListener(this);
// Panel for text fields
filterRow = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
for (int i = 0; i < table.getColumnCount(); i++) {
filterRow.add(new JTextField(" Sum at - " + i));
}
columnMarginChanged(new ChangeEvent(table.getColumnModel()));
getContentPane().add(filterRow, BorderLayout.SOUTH);
}
// Implement TableColumnModelListener methods
// (Note: instead of implementing a listener you should be able to
// override the columnMarginChanged and columMoved methods of JTable)
@Override
public void columnMarginChanged(ChangeEvent e) {
TableColumnModel tcm = table.getColumnModel();
int columns = tcm.getColumnCount();
for (int i = 0; i < columns; i++) {
JTextField textField = (JTextField) filterRow.getComponent(i);
Dimension d = textField.getPreferredSize();
d.width = tcm.getColumn(i).getWidth();
textField.setPreferredSize(d);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
filterRow.revalidate();
}
});
}
@Override
public void columnMoved(TableColumnModelEvent e) {
Component moved = filterRow.getComponent(e.getFromIndex());
filterRow.remove(e.getFromIndex());
filterRow.add(moved, e.getToIndex());
filterRow.validate();
}
@Override
public void columnAdded(TableColumnModelEvent e) {
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
public static void main(String[] args) {
JFrame frame = new TableFilterRow();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
,并将相同的内容添加到“TableFooter”
最简单的方法应该是两个JTable的解决方法或@camickr
的示例
{{1}}
答案 1 :(得分:4)
问题是rendererPane的位置:你的代码中发生的事情是标签位于x / y,靠近上边界线,具有视口rect的完整高度,然后文本被绘制在某处视口的中间,后来被视口覆盖。看到那个效果,使用零行tableModel并使视口不透明。
相反,你必须把它放在边框的底部..一个非常脏(没有做任何事情让它像素更正,在周末跑步:)片段
g.setColor(Color.RED);
int scrollBottom = scroll.getInsets().bottom;
int lowerBorderTop = height - scrollBottom;
g.drawLine(x, height - scrollBottom - 1, width - 10, height - scrollBottom - 1);
g.drawRect(x + 1, height - scrollBottom, width - 10, 10);
Color oldColor = null;
Component cellRendererComponent = null;
int columnWidths = x ;//- scroll.getViewport().getViewRect().x;
for (int column = 0; column < table.getColumnCount(); column++)
{
TableCellRenderer cellRenderer = table.getCellRenderer(0, column);
cellRendererComponent = cellRenderer.getTableCellRendererComponent(table,
getFooterValueAt(column), false, false, 0, column);
if (oldColor == null)
{
oldColor = cellRendererComponent.getBackground();
}
int columnWidth = table.getColumnModel().getColumn(column).getWidth();
cellRendererComponent.setForeground(Color.BLACK);
cellRendererComponent.setBackground(COLORS[column % COLORS.length]);
cellRendererPane.paintComponent(g, cellRendererComponent, scroll, columnWidths,
lowerBorderTop , columnWidth, table.getRowHeight(), false);
columnWidths += columnWidth;
}
if (cellRendererComponent != null)
{
cellRendererComponent.setBackground(oldColor);
}
额外的线条和矩形只是为了看看我们在哪里。顺便说一句,很好的方法: - )
答案 2 :(得分:1)
我从mKorbel扩展了一些代码。 原理是一样的,我感谢mKorbel的概念。 因为我是新手,所以无法发布图片,但此代码提供了接近真实标题的布局。 Buttom标题文本字段与真实标题颜色相同,边框样式相同。 我将实际标头设置为null,因为我想要在标题中添加标题。
public class TableFooter implements TableColumnModelListener {
private JTable table;
private JPanel filterRow;
public JPanel panel;
private Color HeaderColor = Color.getHSBColor(0, 0, (float)0.93);
public TableFooter(JTable mytable) {
GblTools gbl = new GblTools();
table = mytable;
table.setPreferredScrollableViewportSize(table.getPreferredSize());
table.setTableHeader(null);
JScrollPane scrollPane = new JScrollPane(table);
panel = new JPanel();
panel.setLayout(gbl.setLayoutStyle());
scrollPane.setBorder(BorderFactory.createMatteBorder(1,1,0,0,Color.GRAY));
panel.add(scrollPane, gbl.setPosition(0, 0, 1, 1, GblTools.BOTH));
table.getColumnModel().addColumnModelListener(this);
// Panel for text fields
filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
for (int i = 0; i < table.getColumnCount(); i++) {
JTextField temp = new JTextField(" Sum at - " + i);
if(i==0){
temp.setBorder(BorderFactory.createMatteBorder(1,1,1,1,Color.GRAY));
}else{
temp.setBorder(BorderFactory.createMatteBorder(1,0,1,1,Color.GRAY));
}
temp.setHorizontalAlignment(JTextField.CENTER);
temp.setBackground(HeaderColor);
filterRow.add(temp);
}
columnMarginChanged(new ChangeEvent(table.getColumnModel()));
GridBagConstraints position = gbl.setPosition(0, 1, 1, 1, GblTools.HORIZONTAL);
position.insets = new Insets(-3,2,0,0);
panel.add(filterRow, position);
}
// Implement TableColumnModelListener methods
// (Note: instead of implementing a listener you should be able to
// override the columnMarginChanged and columMoved methods of JTable)
@Override
public void columnMarginChanged(ChangeEvent e) {
TableColumnModel tcm = table.getColumnModel();
int columns = tcm.getColumnCount();
for (int i = 0; i < columns; i++) {
JTextField textField = (JTextField) filterRow.getComponent(i);
textField.setEditable(false);
textField.setSelectionColor(HeaderColor);
Dimension d = textField.getPreferredSize();
if(i==0){
d.width = tcm.getColumn(i).getWidth()+1;
}else{
d.width = tcm.getColumn(i).getWidth();
}
textField.setPreferredSize(d);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
filterRow.revalidate();
}
});
}
@Override
public void columnMoved(TableColumnModelEvent e) {
Component moved = filterRow.getComponent(e.getFromIndex());
filterRow.remove(e.getFromIndex());
filterRow.add(moved, e.getToIndex());
filterRow.validate();
}
@Override
public void columnAdded(TableColumnModelEvent e) {
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
public static void main(String[] args) {
TableFooter panel = new TableFooter(new JTable(10, 5));
//frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
JFrame frame = new JFrame();
frame.setContentPane(panel.panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
我使用了我自己的一个类用于GridBagLayuot。 gbl.setPosition(0,1,1,1,GblTools.HORIZONTAL) 设置gridx,gridy,gridwidth,gridheight,fill,应该很容易修复