JTable的页脚 [英] Footer for JTable

查看:48
本文介绍了JTable的页脚的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

JTable缺少支持显示页脚以及每列汇总数据的支持.受到 Oracle/Sun bug数据库中的建议解决方案的启发,我首先采用了在页脚的边框上绘制边框的方法.

JTable lacks support for showing a footer with aggregated data for each column. Inspired by a suggested solution at Oracle/Suns bug database that looked promising, I started with the approach that the footer is painted by the border of the scrollpane.

现在开始时,我只想要一个概念证明",看来我快要准备好了,每列都有一个页脚,宽度是同步的.等等.因此,除了一件事之外,它看起来还不错,没有文字被涂上!我希望从getFooterValueAt返回的虚拟值绘制在每个页脚单元"上,但它们都是空白,只绘制背景(仅用于测试).

Now at first I just want a 'proof of concept' and it seems I'm almost there, each column have a footer, widths are in sync. etc. So it seems to work pretty good except one thing, no text is painted! I expect the dummy values returned from getFooterValueAt painted on each "footer cell" but they're all blank, only the background (just for testing) is painted.

为什么没有绘制任何文字? paintFooter中我的位置/尺寸计算有问题吗?

Why isn't any text painted? Is there something wrong with my position/size calculations in 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();
        }   
    }
}

推荐答案

问题是rendererPane的位置:代码中发生的是标签位于x/y,即靠近具有在视口rect的整个高度上,然后将文本绘制在视口中间的某个位置,然后再由视口覆盖.看到这种效果,使用零行tableModel并使视口不透明.

The problem is the location of the rendererPane: what happens in your code is that the label is located at x/y, that is near the upper borderline having the complete height of the viewport rect, then the text is painted somewhere in the middle of the viewport, and later overpainted by the viewport. The see that effect, use a zero row tableModel and make the viewport not-opaque.

相反,您必须将其放置在边框的底部..很脏(在进入周末之前,没有做任何像素校正):

Instead you have to position it at the lower bottom of the border.. A very dirty (did nothing to get it pixel-correct, on the run into the weekend :) snippet

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

附加的线条和矩形只是为了查看我们在哪里.顺便说一句,不错的方法:-)

the additional line and rectangle is just to see where we are. BTW, nice approach :-)

这篇关于JTable的页脚的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆