包装线,右对齐,自动调整JTable中的行高

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

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

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

这是我的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); } } } 

编辑: 这对我的桌子来说很简单

值得一提的是我曾经躺在一些旧代码中。

它使用带有包装的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(); } } } 

所以,我花了一些时间来对抗这种尝试各种不同事物。

TextAreaRenderer开始。 我更喜欢使用DefaultTableCelllRenderer ,但在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 * Implementation Note * 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 * Implementation Note * for more information. * * @since 1.5 */ public void invalidate() { } /** * Overridden for performance reasons. See the * Implementation Note * for more information. */ public void validate() { } /** * Overridden for performance reasons. See the * Implementation Note * for more information. */ public void revalidate() { } /** * Overridden for performance reasons. See the * Implementation Note * for more information. */ public void repaint(long tm, int x, int y, int width, int height) { } /** * Overridden for performance reasons. See the * Implementation Note * for more information. */ public void repaint(Rectangle r) { } /** * Overridden for performance reasons. See the * Implementation Note * for more information. * * @since 1.5 */ public void repaint() { } /** * Overridden for performance reasons. See the * Implementation Note * for more information. */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } } 

然后我袭击了BlankTable ……

其中一个重大变化是表格现在能够动态计算行的高度。 由于计算成本很高,因此缓存此操作的结果。 但问题是, JTable还拥有自己的内部缓存……这是私有的……所以不要追踪reflection路径,我们需要提供一些补偿来规避这个API产生的一些副作用。

调用setRowHeightJTable调用invalidate自身invalidaterepaint 。 当我们执行计算时,我们想要阻止它,但在某些情况下我们仍然应该允许它们工作(例如当列resize并且JTable invalidated

 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 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需要更新,我还没有测试过对单元格的更改)

初始加载后,我发现渲染是合理的。 由于使用的组件,字体和其他系统问题,你可能是公平的。