JTable的页脚

时间:2012-02-03 15:44:28

标签: java swing jtable

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();
        }   
    }
}

3 个答案:

答案 0 :(得分:4)

  • JComponent/JTextComponents放置到JVievport(有利于MouseEvents

  • JComponent/JTextComponents放置到Glasspane(有利于MouseEvents

  • JComponent/JTextComponents放置到JPanel,然后将JPanel放到JScrollPane,通过工具ComponentListener,您可以将JPanel调整为JViewport's DimensionJViewport'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”

  • 可以写成I don't know how to call it ????

最简单的方法应该是两个JTable的解决方法或@camickr

的示例

enter image description here

{{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,应该很容易修复