JTable中的换行,右对齐,自动调整行高

Posted

技术标签:

【中文标题】JTable中的换行,右对齐,自动调整行高【英文标题】:Wrapping Line, Right Align,Auto Resize Row Height In JTable 【发布时间】:2019-01-19 06:00:45 【问题描述】:

我编写了一个 Java 应用程序,我需要一个具有一些特性的 JTable 类对象:

    自动包装线 右对齐 自动调整行高(这意味着例如一行的大小为 25,但表中另一行的大小为 50,内容大小为一行) 渲染速度必须

但是我的代码运行速度不快并且没有完全的***功能,我在最后一个问题中编写了我的应用程序的最小版本:

这是我的 gui 类:

import javax.swing.*;
import java.awt.*;

public class GUI extends JFrame 
    private BankTable table;
    private JScrollPane scrollPane;

    public GUI()
        super("Bank Table");
        JPanel contentPanel = new JPanel();
        setContentPane(contentPanel);
        contentPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
        setLayout(new BorderLayout());
        setMinimumSize(new Dimension(1000,700));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        makeTable();

        scrollPane = new JScrollPane(table);
        scrollPane.getVerticalScrollBar().setUnitIncrement(50);

        add(scrollPane,BorderLayout.CENTER);

        setVisible(true);
    

    public void makeTable()
        String[][]data= new String[][]"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/01",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/02",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/03",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/04",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/05",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/06",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/07",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/08",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/09",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/10",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/11",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/12",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/13",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/14",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/15",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/16",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/17",
                "0212670003009", "ص 318", "77081111111111111111111111111111111111634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/18",
                "0212670003009", "ص 318", "77081222222222222222222222222222634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/19",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/20",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/21",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/22",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/23",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/24",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/25",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/26",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/27",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/28",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/29",
                "0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/30";

        String[] columns= new String[]"شماره حساب", "اطلاعات اضافه", "مانده","واریز","برداشت","فیش/ حواله","کد شعبه","شرح","ساعت","تاریخ";

        table = new BankTable(data,columns);
        table.updateRowHeights();
        System.out.println("Table");
    

    public static void main(String args[])
        GUI gui = new GUI();
    


这是表格的渲染:

import java.awt.*;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;

public class TextAreaRenderer extends JTextArea
        implements TableCellRenderer 

    public TextAreaRenderer() 
        setLineWrap(true);
        setWrapStyleWord(true);
        setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);    

        setEditable(false);
        setCursor(null);
        setOpaque(false);
        setFocusable(false);
        setLineWrap(true);
        setWrapStyleWord(true);

    

    public Component getTableCellRendererComponent(JTable jTable,
                                                   Object obj, boolean isSelected, boolean hasFocus, int row,
                                                   int column) 
        setText((String)obj);
        setFont(new Font("bnazanin", Font.BOLD, 15));

        return this;
        

这是表类:

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;

public class BankTable extends JTable 

    public BankTable(String[][] data,String[] columns)
        super(data,columns);

        setForeground(Color.BLACK);
        setBounds(60,80,400,600);

        changeTableHeader();

        DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer();
        rightRenderer.setHorizontalAlignment(JLabel.RIGHT);

        TextAreaRenderer textAreaRenderer = new TextAreaRenderer();

        for(int i=0 ; i<columns.length ; i++)
            getColumnModel().getColumn(i).setCellRenderer(textAreaRenderer);
            
    

    public void changeTableHeader()
        getTableHeader().setBackground(new Color(57,77,112));
        getTableHeader().setForeground(Color.WHITE);
        getTableHeader().setFont(new Font("Calibri Light", Font.BOLD, 20));

    

    @Override
    public boolean isCellEditable(int i, int i1) 
        return false;
    

    public void updateRowHeights()
    
        for (int row = 0; row < getRowCount(); row++)
        
            int rowHeight = getRowHeight();

            for (int column = 0; column < getColumnCount(); column++)
            
                Component comp = prepareRenderer(getCellRenderer(row, column), row, column);
                rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
            
            setRowHeight(row, rowHeight + 20);
        
        

编辑: This is a simple for my table

【问题讨论】:

"Autowrapper" - Java-Swing adding multiple lines in jtable's cell “右对齐” - JLabel#setHorizontalAlignment “自动调整行高” - Vertical and Horizontal Allignment in JTextPane 我的代码具有自动换行单元格,但是我之前使用过此代码,但渲染速度在两个代码中都没有解决。 @MadProgrammer “渲染速度必须很高” - 好吧,这将是一个问题,因为对于每一行,您需要计算每列的高度并确定高度行需要。这应该谨慎进行,并且仅在您绝对需要时才进行 - 例如当模型更改或模型本身更改或 JTable 无效时。然后,您将需要缓存结果,以便将来可以快速完成任何渲染 【参考方案1】:

因为这里有价值的是一些旧代码。

它使用带换行的 JTextArea,但将文本区域中的行限制为 2 行。然后您可以滚动文本区域以查看其他行。

也许这种方法有助于满足您的要求?

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class TableTextArea extends JFrame

    public TableTextArea()
    
        JTable table = new JTable(40, 5);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        table.setRowHeight(40);
        table.setValueAt("one two three four five six seven eight nine ten", 0, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 1, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 0, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 2, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 3, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 4, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 5, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 6, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 7, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 8, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 9, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 10, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 11, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 12, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 13, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 14, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 15, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 16, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 17, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 18, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 19, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 20, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 21, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 22, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 23, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 24, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 25, 4);
        table.setValueAt("one two three four five six seven eight nine ten", 26, 2);
        table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 27, 2);
        table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 28, 4);
        JScrollPane scrollPane = new JScrollPane( table );
        add( scrollPane );

        //  Override default renderer for a specific column

        TableCellRenderer renderer = new TextAreaRenderer();
        table.getColumnModel().getColumn(2).setCellRenderer( renderer );
        table.getColumnModel().getColumn(4).setCellRenderer( renderer );
        table.changeSelection(0, 0, false, false);
    

    public static void main(String[] args)
    
        SwingUtilities.invokeLater(new Runnable()
        
            public void run()
            
                TableTextArea frame = new TableTextArea();
                frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
                frame.pack();
                frame.setLocationRelativeTo( null );
                frame.setVisible(true);
            
        );
    

    /*
    **
    */
    class TextAreaRenderer implements TableCellRenderer
    
        private JTextArea renderTextArea;
        private JScrollPane renderScrollPane;
        private JTextArea focusTextArea;
        private JScrollPane focusScrollPane;
        private boolean firstTime = true;

        public TextAreaRenderer()
        
            renderTextArea = new JTextArea();
            renderTextArea.setEditable( false );
            renderTextArea.setLineWrap( true );
            renderTextArea.setWrapStyleWord( true );
            renderScrollPane = new JScrollPane( renderTextArea );
            renderScrollPane.setBorder(null);
            renderScrollPane.revalidate();

            focusTextArea = new JTextArea();
            focusTextArea.setEditable( false );
            focusTextArea.setLineWrap( true );
            focusTextArea.setWrapStyleWord( true );
            focusScrollPane = new JScrollPane( focusTextArea );
            focusScrollPane.setBorder(null);
        

        public Component getTableCellRendererComponent(
            final JTable table, Object value, boolean isSelected, boolean hasFocus, final int row, final int column)
        
            //  For some reason the scrollbars don't appear on the first cell renderered.
            //  Forcing a repaint of the cell seems to fix the problem.

            if (firstTime)
            
                firstTime = false;
                Rectangle cellRectangle = table.getCellRect(row, column, false);
//              renderScrollPane.setBounds(cellRectangle);
//              table.add(renderScrollPane);
//              renderScrollPane.revalidate();
                table.repaint(cellRectangle);
            

            System.out.println(row + " :: " + column + " : " + hasFocus);
            table.remove(focusScrollPane);

            renderTextArea.setText( value != null ? value.toString() : "" );
            renderTextArea.setCaretPosition(0);

            if (hasFocus)
            
                renderTextArea.setBackground( table.getSelectionBackground() );

                SwingUtilities.invokeLater( new Runnable()
                
                    public void run()
                    
                        addRealTextAreaToTable(table, row, column);
                    
                );
            
            else if (isSelected)
                renderTextArea.setBackground( table.getSelectionBackground() );
            else
                renderTextArea.setBackground( table.getBackground() );

            return renderScrollPane;
        

        private void addRealTextAreaToTable(JTable table, int row, int column)
        
            System.out.println(row + " :: " + column);
            Object value = table.getValueAt(row, column);
            focusTextArea.setText( value != null ? value.toString() : "" );
            focusTextArea.setCaretPosition(0);
//          focusTextArea.setBackground( table.getBackground() );
            focusTextArea.setBackground( table.getSelectionBackground() );
            Rectangle cellRectangle = table.getCellRect(row, column, false);
            focusScrollPane.setBounds(cellRectangle);
            table.add(focusScrollPane);
            focusScrollPane.revalidate();
            focusTextArea.requestFocusInWindow();
        
    

【讨论】:

值得尝试,但这段代码很糟糕,因为我希望行的大小自动更改,就像您的代码在某些单元格中有滚动一样。请看一下我现在上传到问题的简单表格【参考方案2】:

所以,我花了一些时间来反对这个尝试不同的真实性。

TextAreaRenderer 开头。我更喜欢使用DefaultTableCelllRenderer,但在玩弄了它之后,我无法让它令人满意地工作。因此,相反,我采用了它自己的大部分优化并将它们应用于TextAreaRenderer 本身。

您真正想做的一件事是减少每个getTableCellRendererComponent 将进行的更改量。 JTextArea 已经是一个复杂的组件了。

我删除了setOpaque 调用并将setFont 调用移至构造函数。我还添加了选择支持(这是我测试的一部分)

public class TextAreaRenderer extends JTextArea
        implements TableCellRenderer 

    public TextAreaRenderer() 
        setLineWrap(true);
        setWrapStyleWord(true);
        setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

        setEditable(false);
        setCursor(null);
        //setOpaque(false);
        setFocusable(false);
        setLineWrap(true);
        setWrapStyleWord(true);
        setFont(new Font("bnazanin", Font.BOLD, 15));

        getCaret().setBlinkRate(0);
    

    public Component getTableCellRendererComponent(JTable jTable,
            Object obj, boolean isSelected, boolean hasFocus, int row,
            int column) 
        setText((String) obj);

        if (isSelected) 
            System.out.println("!!Selected");
            setBackground(jTable.getSelectionBackground());
            setForeground(jTable.getSelectionForeground());
         else 
            setBackground(jTable.getBackground());
            setForeground(jTable.getForeground());
        

        return this;
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     */
    public boolean isOpaque() 
        Color back = getBackground();
        Component p = getParent();
        if (p != null) 
            p = p.getParent();
        

        // p should now be the JTable.
        boolean colorMatch = (back != null) && (p != null)
                && back.equals(p.getBackground())
                && p.isOpaque();
        return !colorMatch && super.isOpaque();
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     *
     * @since 1.5
     */
    public void invalidate() 
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     */
    public void validate() 
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     */
    public void revalidate() 
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     */
    public void repaint(long tm, int x, int y, int width, int height) 
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     */
    public void repaint(Rectangle r) 
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     *
     * @since 1.5
     */
    public void repaint() 
    

    /**
     * Overridden for performance reasons. See the
     * <a href="#override">Implementation Note</a>
     * for more information.
     */
    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) 
    


然后我攻击了BlankTable...

其中一个重大变化是表格现在能够动态计算行的高度。因为计算开销很大,所以这个操作的结果被缓存了。不过,问题是JTable 也有它自己的内部缓存......这是私有的?......所以与其追踪反射路线,我们需要提供一些补偿来规避这个 API 的一些副作用创建。

当调用 setRowHeight 时,JTable 调用 invalidaterepaint 本身。当我们执行我们的计算时,我们希望停止它,但是在某些情况下我们仍然应该允许工作(例如当列被调整大小并且JTableinvalidated

public class BankTable extends JTable 

    public BankTable(String[][] data, String[] columns) 
        super(data, columns);

        changeTableHeader();

        setGridColor(Color.RED);

        TableCellRenderer textAreaRenderer = new TextAreaRenderer();

        for (int i = 0; i < columns.length; i++) 
            getColumnModel().getColumn(i).setCellRenderer(textAreaRenderer);
        
    

    public void changeTableHeader() 
        getTableHeader().setBackground(new Color(57, 77, 112));
        getTableHeader().setForeground(Color.WHITE);
        getTableHeader().setFont(new Font("Calibri Light", Font.BOLD, 20));

    

    @Override
    public boolean isCellEditable(int i, int i1) 
        return false;
    

    private Map<Integer, Integer> rowMap = new HashMap<>();
    private boolean quietUpdate = false;
    private boolean forceUpdate = false;

    @Override
    public void invalidate() 
        rowMap.clear();
        forceUpdate = true;
        super.invalidate();
        forceUpdate = false;
    

    @Override
    protected void resizeAndRepaint() 
        if (quietUpdate) 
            if (forceUpdate) 
                super.resizeAndRepaint();
            
         else 
            super.resizeAndRepaint();
        
    

    @Override
    public int getRowHeight(int row) 
        Integer rowHeight = rowMap.get(row);
        if (rowHeight == null) 
            TableColumnModel model = getColumnModel();
            rowHeight = getRowHeight();
            for (int column = 0; column < getColumnCount(); column++) 
                int colWidth = model.getColumn(column).getWidth();
                Component comp = prepareRenderer(getCellRenderer(row, column), row, column);
                comp.setSize(new Dimension(colWidth, Integer.MAX_VALUE));
                rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
            
            rowMap.put(row, rowHeight);
            quietUpdate = true;
            setRowHeight(row, rowHeight);
            quietUpdate = false;
        
        return rowHeight;
    

    @Override
    public void tableChanged(TableModelEvent e) 
        if (rowMap == null) 
            super.tableChanged(e);
            return;
        
        if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) 
            rowMap.clear();
            super.tableChanged(e);
            return;
        

        if (e.getType() == TableModelEvent.INSERT) 
            super.tableChanged(e);
            return;
        

        int modelColumn = e.getColumn();
        int start = e.getFirstRow();
        int end = e.getLastRow();

        if (e.getType() == TableModelEvent.DELETE || e.getType() == TableModelEvent.UPDATE) 
            for (int row = start; row <= end; row++) 
                rowMap.remove(row);
            
            super.tableChanged(e);
            return;
        

        if (end == Integer.MAX_VALUE) 
            rowMap.clear();
        
        super.tableChanged(e); //To change body of generated methods, choose Tools | Templates.
    

还有一些其他方面可能需要进一步优化(我没有测试排序表,所以 tableChanged 需要更新,我也没有测试单元格的更改)

在初始加载后,我发现渲染是合理的。由于使用中的组件、字体等系统问题,你的里程可能还算公平。

【讨论】:

此表具有我需要的所有功能,但现在渲染速度非常低。所以问题没有解决。但是底部链接中有许多更改的答案代码运行得很好......但是JTable本身不适合复杂使用***.com/questions/51797852/… 天啊,对我来说,渲染效果比你原来的改进了很多——不知道你在做什么 我测试了您的代码,但有所改进,但渲染速度仍然很慢。 我不确定与什么相比。一般来说,我的测试速度在一个合理的水平上,不会比我通常体验的多或少——尽管解决方案比我需要做的大多数事情更复杂——只要你只是滚动,事情就会在短时间内变得混乱调整表/列的大小,但(对我来说)是需要重新计算行高的预期开销。文本布局已经是一个复杂的过程。我会考虑尝试降低整体解决方案的复杂性,并以较小的增量重新引入它们,以查看某些问题可能出现的位置 好的。反正坦克。但我需要更快的速度...... :)

以上是关于JTable中的换行,右对齐,自动调整行高的主要内容,如果未能解决你的问题,请参考以下文章

怎么样为Excel2010表格设置自动换行

Excel project中,如何设置任务的文字自动换行

JTable单元格自动换行

EXCEL中如何输入文字且文字不换行

LaTeX 公式过长 换行和对齐

Label的换行问题