Нижний колонтитул для JTable - PullRequest
       0

Нижний колонтитул для JTable

4 голосов
/ 03 февраля 2012

В JTable отсутствует поддержка отображения нижнего колонтитула с агрегированными данными для каждого столбца. Вдохновленный предложенным решением на базе данных ошибок Oracle / Suns , которое выглядело многообещающе, я начал с подхода, согласно которому нижний колонтитул закрашивается границей полосы прокрутки.

Сначала я просто хочу «подтверждение концепции», и кажется, что я почти на месте, каждый столбец имеет нижний колонтитул, ширина синхронизирована. и т.д. Так что, кажется, работает довольно хорошо, за исключением одного, текст не нарисован! Я ожидаю, что фиктивные значения, возвращаемые из 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 ]

4 голосов
/ 03 февраля 2012

Проблема в расположении rendererPane: то, что происходит в вашем коде, заключается в том, что метка расположена в точке x / y, то есть около верхней границы, имеющей полную высоту прямоугольника области просмотра, затем текст закрашивается где-то в середина окна просмотра, а затем закрашена окном просмотра. Эффект видеть, используйте нулевую строку 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);
        }

дополнительная линия и прямоугольник просто чтобы увидеть, где мы находимся. Кстати, хороший подход: -)

4 голосов
/ 03 февраля 2012
  • место JComponent/JTextComponents до JVievport (преимущество берет MouseEvents)

  • место JComponent/JTextComponents до Glasspane (преимущество берет MouseEvents)

  • место JComponent/JTextComponents до JPanel, затем это JPanel до JScrollPane, с помощью орудий ComponentListener вы можете изменить размер JPanel до JViewport's Dimension требуется только изменить размер меньшего JPanel до JViewport's Dimension

  • во всех случаях требуется добавить собственный Borders к JScrollPane и с таким же для "TableFooter"

  • можно написали я не знаю как это назвать ????

Самый простой способ - это обходной путь для двух JTables или, например, @ camickr

enter image description here

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);
    }
}
1 голос
/ 25 декабря 2013

Я немного расширил код от mKorbel.Принцип тот же, и я благодарю mKorbel за концепцию.Не могу опубликовать изображение, поскольку я новичок, но этот код дает макет, близкий к реальному заголовку.Текстовые поля заголовка кнопки имеют тот же цвет и тот же стиль границы, что и настоящий заголовок.Я установил настоящий заголовок на ноль, так как я хотел заголовок в нижней части.

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, должно быть легко исправить

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...