Pertanyaan Footer untuk JTable


JTable tidak memiliki dukungan untuk menampilkan footer dengan data agregat untuk setiap kolom. Terinspirasi oleh solusi yang disarankan di Oracle / Suns bug database yang tampak menjanjikan, saya mulai dengan pendekatan bahwa footer dicat oleh perbatasan dari scrollpane.

Sekarang pada awalnya saya hanya ingin 'bukti konsep' dan sepertinya saya hampir di sana, setiap kolom memiliki footer, lebar yang sinkron. dll. Jadi sepertinya bekerja cukup baik kecuali satu hal, tidak ada teks yang dilukis! Saya berharap nilai dummy kembali getFooterValueAt dilukis pada setiap "footer cell" tetapi semuanya kosong, hanya latar belakang (hanya untuk pengujian) yang dilukis.

Mengapa tidak ada teks yang dilukis? Apakah ada yang salah dengan perhitungan posisi / ukuran saya di 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
2018-02-03 15:44


asal


Jawaban:


Masalahnya adalah lokasi rendererPane: apa yang terjadi dalam kode Anda adalah bahwa label terletak di x / y, yaitu di dekat batas atas yang memiliki ketinggian lengkap dari viewport rect, maka teks tersebut dilukis di suatu tempat di tengah-tengah viewport, dan kemudian overpainted oleh viewport. Melihat efek itu, gunakan zero row tableModel dan buat viewport tidak-buram.

Alih-alih Anda harus memposisikannya di bagian bawah bawah perbatasan. Sangat kotor (tidak melakukan apa pun untuk membuatnya benar-benar piksel, pada saat berlari ke snippet akhir pekan)

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

garis tambahan dan persegi panjang hanya untuk melihat di mana kita berada. BTW, pendekatan yang bagus :-)


4
2018-02-03 17:36



  • tempat JComponent/JTextComponents untuk JVievport (ambil keuntungan MouseEvents)

  • tempat JComponent/JTextComponents untuk Glasspane  (ambil keuntungan MouseEvents)

  • tempat JComponent/JTextComponents untuk JPanel, lalu ini JPanel ke JScrollPane, dengan alat ComponentListener Anda dapat mengubah ukuran JPanel ke JViewport's Dimension hanya diperlukan mengubah ukuran JPanel lebih kecil hingga JViewport's Dimension 

  • dalam semua hal diperlukan tambahkan sendiri Borders untuk JScrollPane dan dengan yang sama untuk "TableFooter"

  • mungkin menulis Saya tidak tahu bagaimana menyebutnya ???? 

cara paling sederhana harus solusi untuk dua JTables atau contoh oleh @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
2018-02-03 16:27



Saya Perpanjang kode dari mKorbel sedikit. Prinsipnya sama, dan saya berterima kasih kepada mKorbel untuk konsepnya. Tidak dapat memposting gambar karena saya baru, tetapi kode ini memberikan tata letak yang mendekati header nyata. Buttom header textfields adalah warna yang sama, dan style border yang sama dengan header asli. Saya mengatur header asli ke null, karena saya ingin sebuah header di 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);
 }

}

Saya menggunakan salah satu kelas saya sendiri untuk GridBagLayuot. gbl.setPosition (0, 1, 1, 1, GblTools.HORIZONTAL) set gridx, gridy, gridwidth, gridheight, isi, harus mudah diperbaiki


1
2017-12-25 13:07