【问题标题】:JTable header text wrapping for multiline header (custom TableCellRenderer)多行标题的 JTable 标题文本换行(自定义 TableCellRenderer)
【发布时间】:2017-04-29 21:34:14
【问题描述】:

如何获得一个多行 JTable 标题,其中标题列正确放大以适应某些文本,然后换行?

如下图所示:

目前搜索上述需求返回了很多解决方案,但没有一个真正解决问题:

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

由于几个原因,该解决方案并不优雅,主要是因为在可变列名称的情况下,我需要将字符串传递给一个函数,该函数去除空格并用&lt;br&gt; 符号替换它们,但是如果列文本包含非常短的文本,单独出现在一行中。

我需要确定一列的最小和最大长度,然后才能使文本居中成为可能,上述解决方案很快就会变得过度设计和难以管理。

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

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

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

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

-或-

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

【问题讨论】:

    标签: java swing jtable jtableheader


    【解决方案1】:

    这里也使用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;
      }
    }
    

    【讨论】:

    • 效果很好,唯一的问题是它使用 JTextArea 在标题中显示文本,因此列标题样式将丢失,例如将使用白色背景。
    • 我编辑了答案,使标题看起来像 JTable 的默认标题。
    • @sarah.ferguson 尝试将 JTextArea 的背景设置为 JTable.getBackground()
    • 为了给标题提供与JTable 相同的颜色,我使用了setOpaque(false)。请参阅上面的编辑代码。
    【解决方案2】:

    您需要一个能够像 JTextArea 一样自动换行其内容的组件。 我从您的 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;
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      1. 您无需手动断开列名。
      2. 当您调整列和/或窗口的大小时,列会动态自动换行。
      3. 标题外观将自动与安装的外观保持一致。
      4. 与我见过的其他解决方案不同,即使第一列没有换行(如下例所示),它也能正常工作。

      但是,它有以下缺点:它为每一列创建一个未使用的 JTableHeader 对象,所以它有点不雅,如果你有很多列,可能不适合。

      基本思想是将列名包装在&lt;html&gt; 标记中,并且至关重要的是,每个TableColumn 都有自己的TableCellRenderer 对象。

      在深入调试 Swing 表头布局管道的内部后,我得出了这个解决方案。无需过多讨论,问题是如果TableColumns 没有定义 headerRenderer,则每个列标题单元格都使用相同的默认渲染器。用于JTableHeader 的布局代码只需询问 first 列标题的渲染器的首选大小(参见上面的功能 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();});
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-07-24
        • 1970-01-01
        • 2011-01-23
        • 1970-01-01
        • 2017-01-02
        • 1970-01-01
        • 1970-01-01
        • 2021-07-31
        • 1970-01-01
        相关资源
        最近更新 更多