Footer for JTable

JTable does not support footer support with aggregated data for each column. Inspired by the proposed solution in an Oracle / Suns database , which looked promising, I started with the approach that the footer is colored with a scroll border.

Now at first I just want a “proof of concept”, and it seems like I'm almost there, each column has a footer, the width is in sync. etc. Thus, it works very well, except for one, the text is not drawn! I expect that the dummy values ​​returned with getFooterValueAt are drawn on each "bottom column", but they are all empty, only the background (for testing only) is drawn.

Why is the text not colored? Is there something wrong with my location / 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(); } } } 
+4
source share
3 answers

The problem is the rendererPane location: what happens in your code is that the label is located at x / y, that is, next to the top border having the full height of the viewport rectangle, then the text is painted somewhere in the middle of the viewport and then repainted in the viewport. See This effect, use a table with a zero row and make the visibility not opaque.

Instead, you should place it at the bottom of the border. Very dirty (did nothing to get the correct image in pixels, on the run on 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); } 

extra line and rectangle - it's just to see where we are. BTW, a good approach :-)

+4
source
  • put JComponent/JTextComponents in JVievport ( MouseEvents advantage)

  • put JComponent/JTextComponents in Glasspane (advantage of MouseEvents )

  • put the JComponent/JTextComponents in the JPanel , then this JPanel in the JScrollPane , implementing the ComponentListener , you can resize the JPanel to JViewport Dimension , only you need to resize the smaller JPanel to JViewport Dimension

  • in all cases, you need to add your own Borders to JScrollPane and with the same for "TableFooter"

  • can I write I don’t know what to call him ????

The easiest way should be a workaround for two JTables or examples using @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); } } 
+4
source

I expanded the code a bit from mKorbel. The principle is the same, and I thank mKorbel for the concept. I can’t post the picture, because I'm new, but this code gives a layout close to the real title. Buttom header text fields have the same color and border style as the actual header. I set the true title to zero since I need the title in buttom.

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

}

I used one of my own classes for GridBagLayuot. gbl.setPosition (0, 1, 1, 1, GblTools.HORIZONTAL) sets gridx, gridy, gridwidth, gridheight, fill, should be easily fixed.

+1
source

Source: https://habr.com/ru/post/1394651/


All Articles