JTable header 多行文本换行 header(自定义 TableCellRenderer)
JTable header text wrapping for multiline header (custom TableCellRenderer)
如何获得多行 JTable header,其中 header 列正确放大以适合某些文本,然后换行?
如下所示:
目前正在搜索以上需求returns很多方案其中none确实解决了问题:
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>
符号替换它们,但是如果列文本包含非常短的文本,单独一行显示。
我需要确定列的最小和最大长度,然后才能使文本居中成为可能,上述解决方案很快就会变得过度设计和难以管理。
http://www.java2s.com/Code/Java/Swing-Components/MultiLineHeaderTable.htm
http://www.java2s.com/Code/Java/Swing-Components/MultiLineHeaderExample.htm
以上解决方案需要手动创建一个 header 数组,其中的单词已经正确拆分为:
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?
按照上述解决方案手动设置 header 高度只是我想做的事情的一半,因为这样文本仍然无法正确换行,决定高度仍然不可行。
目前我所能做的就是创建一个自定义的 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;
}
}
您需要一个能够自动换行其内容的组件,例如 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;
}
}
这里也使用了 JTextArea
并且在调整 table 大小时也调整了页眉高度。正确计算table页眉高度的关键是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;
}
}
这是另一种方法。该方案具有以下优点:
- 您无需手动拆分列名。
- 当您调整列的大小时,列动态 word-wrap and/or window。
- header外观会自动与安装的look-and-feel保持一致。
- 与我见过的其他解决方案不同,即使第一列没有换行(如下例所示),此方法也有效。
但是,它有以下缺点:它为每一列创建了一个未使用的 JTableHeader object,所以它有点不优雅,如果你有很多列,可能不合适table。
基本思想是将列名称包装在 <html>
标记中,而且至关重要的是,每个 TableColumn
都有自己的 TableCellRenderer
object.
在深入调试 Swing table header 布局管道后,我找到了这个解决方案。不用过多纠结,问题是如果 TableColumn
没有定义 header 渲染器,则每个列 header 单元格都使用相同的默认渲染器。用于 JTableHeader
的布局代码只麻烦向 第一 列的渲染器询问 header 其首选大小(参见上面的功能 4),并且因为renderer 是 re-used,对其 setText()
方法的调用会触发为标签创建一个新的 View
,由于我太累了甚至不想解释的原因,导致header 渲染器始终报告其首选 unwrapped 高度。
这是一个quick-and-dirty proof-of-concept:
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();});
}
}
如何获得多行 JTable header,其中 header 列正确放大以适合某些文本,然后换行?
如下所示:
目前正在搜索以上需求returns很多方案其中none确实解决了问题:
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>
符号替换它们,但是如果列文本包含非常短的文本,单独一行显示。
我需要确定列的最小和最大长度,然后才能使文本居中成为可能,上述解决方案很快就会变得过度设计和难以管理。
http://www.java2s.com/Code/Java/Swing-Components/MultiLineHeaderTable.htm
http://www.java2s.com/Code/Java/Swing-Components/MultiLineHeaderExample.htm
以上解决方案需要手动创建一个 header 数组,其中的单词已经正确拆分为:
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?
按照上述解决方案手动设置 header 高度只是我想做的事情的一半,因为这样文本仍然无法正确换行,决定高度仍然不可行。
目前我所能做的就是创建一个自定义的 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;
}
}
您需要一个能够自动换行其内容的组件,例如 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;
}
}
这里也使用了 JTextArea
并且在调整 table 大小时也调整了页眉高度。正确计算table页眉高度的关键是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;
}
}
这是另一种方法。该方案具有以下优点:
- 您无需手动拆分列名。
- 当您调整列的大小时,列动态 word-wrap and/or window。
- header外观会自动与安装的look-and-feel保持一致。
- 与我见过的其他解决方案不同,即使第一列没有换行(如下例所示),此方法也有效。
但是,它有以下缺点:它为每一列创建了一个未使用的 JTableHeader object,所以它有点不优雅,如果你有很多列,可能不合适table。
基本思想是将列名称包装在 <html>
标记中,而且至关重要的是,每个 TableColumn
都有自己的 TableCellRenderer
object.
在深入调试 Swing table header 布局管道后,我找到了这个解决方案。不用过多纠结,问题是如果 TableColumn
没有定义 header 渲染器,则每个列 header 单元格都使用相同的默认渲染器。用于 JTableHeader
的布局代码只麻烦向 第一 列的渲染器询问 header 其首选大小(参见上面的功能 4),并且因为renderer 是 re-used,对其 setText()
方法的调用会触发为标签创建一个新的 View
,由于我太累了甚至不想解释的原因,导致header 渲染器始终报告其首选 unwrapped 高度。
这是一个quick-and-dirty proof-of-concept:
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();});
}
}