多行标题的JTable标题文本换行(自定义TableCellRenderer)

时间:2016-12-14 14:03:20

标签: java swing jtable jtableheader

如何获取多行JTable标题,其中标题列正确放大以适合某些文本然后换行到新行?

如下所示:

wrapping column header

目前正在寻找上述要求会返回许多解决方案,而这些解决方案都没有真正解决问题:

http://www.javarichclient.com/multiline-column-header/

Creating multi-line header for JTable

Java JTable header word wrap

以上解决方案都建议使用HTML代码,例如:

String[] columnNames = {
    "<html><center>Closing<br>Date</html>",
    "<html><center>Open<br>Price</html>",
    "<html>Third<br>column</html>"
};

由于几个原因,该解决方案并不优雅,主要是因为在变量列名称的情况下,我需要将字符串传递给一个剥离空格并用<br>符号替换它们的函数,但是如果列text包含非常短的文本,显示在它自己的行中。

我需要确定列的最小和最大长度,然后才能使文本居中成为可能,上述解决方案很快变得过度工程化并且无法管理。

http://www.java2s.com/Code/Java/Swing-Components/MultiLineHeaderTable.htm

http://www.java2s.com/Code/Java/Swing-Components/MultiLineHeaderExample.htm

multilineheader2

以上解决方案需要手动创建一个标题数组,其中的单词已正确拆分,如下所示:

  public static Object[][] tableHeaders = new Object[][] {
      new String[] { "Currency" },
      new String[] { "Yesterday's", "Rate" },
      new String[] { "Today's", "Rate" },
      new String[] { "Rate", "Change" } };

-OR -

DefaultTableModel dm = new DefaultTableModel();
    dm.setDataVector(
        new Object[][] { { "a", "b", "c" }, { "A", "B", "C" } },
        new Object[] { "1st\nalpha", "2nd\nbeta", "3rd\ngamma" });

仍然不优雅,因为列名中的变量文本不可行。

How to change JTable header height?

手动设置标题高度,如上面的解决方案只是我想要做的一半,因为然后文本仍然无法正确换行,并且决定高度仍然不可行。

目前我所能做的只是创建一个自定义的TableCellRenderer,但还没有解决方案:

import java.awt.Component;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import java.util.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import javax.swing.*;
import javax.swing.table.*;

/**
 * @version 1.0 11/09/98
 */
public class MultiLineHeaderExample extends JFrame
{

    MultiLineHeaderExample()
    {
        super("Multi-Line Header Example");

        DefaultTableModel dm = new DefaultTableModel();
        dm.setDataVector(new Object[][]
        {
            {
                "a", "b", "c"
            },
            {
                "A", "B", "C"
            }
        },
        new Object[]
                {
                    "My First Column, Very Long But Space Separated", "short col", "VeryLongNoSpaceSoShouldSomeHowWrap"
        });

        JTable table = new JTable(dm);
        MultiLineHeaderRenderer renderer = new MultiLineHeaderRenderer();
        Enumeration enumK = table.getColumnModel().getColumns();
        while (enumK.hasMoreElements())
        {
            ((TableColumn) enumK.nextElement()).setHeaderRenderer(renderer);
        }
        JScrollPane scroll = new JScrollPane(table);
        getContentPane().add(scroll);
        setSize(400, 110);
        setVisible(true);
    }

    public static void main(String[] args)
    {
        MultiLineHeaderExample frame = new MultiLineHeaderExample();
        frame.addWindowListener(new WindowAdapter()
        {
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }
}

class MultiLineHeaderRenderer extends JList implements TableCellRenderer
{

    public MultiLineHeaderRenderer()
    {
        ListCellRenderer renderer = getCellRenderer();
        ((JLabel) renderer).setHorizontalAlignment(JLabel.CENTER);
        setCellRenderer(renderer);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column)
    {
        setFont(table.getFont());
        String str = (value == null) ? "" : value.toString();
        BufferedReader br = new BufferedReader(new StringReader(str));
        String line;
        Vector v = new Vector();
        try
        {
            while ((line = br.readLine()) != null)
            {
                v.addElement(line);
            }
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
        setListData(v);
        return this;
    }
}

3 个答案:

答案 0 :(得分:5)

这里也使用JTextArea,并在调整表格大小时调整页眉高度。正确计算表头高度的关键是setSize(width, getPreferredSize().height);

class MultiLineTableHeaderRenderer extends JTextArea implements TableCellRenderer
{
  public MultiLineTableHeaderRenderer() {
    setEditable(false);
    setLineWrap(true);
    setOpaque(false);
    setFocusable(false);
    setWrapStyleWord(true);
    LookAndFeel.installBorder(this, "TableHeader.cellBorder");
  }

  @Override
  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    int width = table.getColumnModel().getColumn(column).getWidth();
    setText((String)value);
    setSize(width, getPreferredSize().height);
    return this;
  }
}

答案 1 :(得分:2)

你需要一个能够像JTextArea一样对其内容进行自动包装的Conponent。 我从SSCCE更改了单元格渲染器,因此最初有效,但它有一个讨厌的调整大小行为。

 class MultiLineHeaderRenderer extends JTextArea implements TableCellRenderer {
    public MultiLineHeaderRenderer()
    {
        setAlignmentY(JLabel.CENTER);
        setLineWrap(true);
        setWrapStyleWord(true);
        setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK),
                BorderFactory.createEmptyBorder(3,3,3,3)
                ));

    }

    @Override
    public Component getTableCellRendererComponent(JTable table,
            Object value,
            boolean isSelected,
            boolean hasFocus,
            int row,
            int column) {
        setFont(table.getFont());
        String str = (value == null) ? "" : value.toString();
        setText(str);
        int columnWidth= getColumnWidth();
        setRows(str.length()/columnWidth);
        return this;
    }
}

答案 2 :(得分:1)

这是另一种方法。该解决方案具有以下优点:

  1. 您无需手动中断列名称。
  2. 在调整列和/或窗口大小时,列会动态自动换行。
  3. 标题外观将自动与已安装的外观保持一致。
  4. 与我见过的其他解决方案不同,即使第一列不包装也是如此(如下例所示)。
  5. 它有以下缺点:它为每列创建一个未使用的JTableHeader对象,因此它有点不优雅,如果你有很多列可能不适合。

    基本思想是将列名包装在<html>标记中,而且至关重要的是,每个TableColumn都会获得自己的TableCellRenderer对象。

    在深入调试Swing表头布局管道的内容之后,我来到了这个解决方案。如果TableColumn没有定义headerRenderer,则问题是如果没有过多地进入杂草,则每个列标题单元格都使用相同的默认渲染器。用于JTableHeader的布局代码只是难以向渲染器询问第一个列标题的首选大小(请参阅上面的功能4.),并且因为渲染器被重复使用,调用其setText()方法会触发为标签创建新的View,由于我太累而无法考虑解释,导致标题渲染器始终报告其首选项解开身高。

    这是一个快速而肮脏的概念验证:

    package scratch;
    
    import java.util.*;
    import javax.swing.*;
    import javax.swing.table.*;
    
    @SuppressWarnings("serial")
    public class WordWrappingTableHeaderDemo extends JFrame {
    
        class DemoTableModel extends AbstractTableModel {
    
            private ArrayList<String> wrappedColumnNames = new ArrayList<String>(); 
            private int numRows;
    
            DemoTableModel(List<String> columnNames, int numRows) {
                for (String name: columnNames)
                    wrappedColumnNames.add("<html>" + name + "</html>");
                this.numRows = numRows;
            }
    
            public int getRowCount() {
                return numRows;
            }
    
            public int getColumnCount() {
                return wrappedColumnNames.size();
            }
    
            public Object getValueAt(int rowIndex, int columnIndex) {
                return Integer.valueOf(10000 + (rowIndex + 1)*(columnIndex + 1));
            }
    
            public String getColumnName(int column) {
                return wrappedColumnNames.get(column);
            }
    
            public Class<?> getColumnClass(int columnIndex) {
                return Integer.class;
            }
        }
    
        public WordWrappingTableHeaderDemo() {
    
            DefaultTableColumnModel tableColumnModel = new DefaultTableColumnModel() {
                public void addColumn(TableColumn column) {
                    // This works, but is a bit kludgey as it creates an unused JTableHeader object for each column:
                    column.setHeaderRenderer(new JTableHeader().getDefaultRenderer());
                    super.addColumn(column);
                }
            };
    
            JTable table = new JTable();
            table.setFillsViewportHeight(true);;
            table.setColumnModel(tableColumnModel);
            table.setModel(
                    new DemoTableModel(Arrays.asList("Name", "The Second Column Name is Very Long", "Column Three"), 20));
            getContentPane().add(new JScrollPane(table));
        }
    
        public static void createAndShowGUI() {
            WordWrappingTableHeaderDemo app = new WordWrappingTableHeaderDemo();
            app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            app.setLocationByPlatform(true);
            app.pack();
            app.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {createAndShowGUI();});
        }
    }