我表中一列的单元格是HTML字符串。 HTML用于提供一些颜色指示。通常,列的宽度足以包含整个字符串。但是当它还不够时,字符串很好地剪切在单词边界上。这是期望的行为。使用默认的单元格渲染器。
我注意到偶尔会有一些与表的交互触发渲染器包装字符串。据我所知,包装HTML字符串是JLabel
从DefaultTableCellRenderer
派生的正常行为。不清楚的是,为什么这种行为如此不一致以及是什么引发了它的偏差。 JLabel
来回跳跃的原因是什么,好像它经常被重新测量一样?有关示例,请参见附图。
要解决此问题,我可以将<nobr>
添加到HTML字符串以防止换行,或使用更复杂的渲染器来呈现彩色字符串。但我想知道是否有办法让JLabel
玩得很好。
我设法将整个案例简化为一个简单的例子。我要重现的问题是单击各行以更改选择。
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
public class TestTable extends JPanel{
public TestTable() {
setLayout(new BorderLayout());
Object[][] rows = {
{ "<html><font color=red>1 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"},
{ "<html><font color=green>2 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"},
{ "<html><font color=blue>3 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"},
{ "<html><font color=red>4 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"},
{ "<html><font color=green>5 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"},
};
Object[] columns = {"Column"};
DefaultTableModel model = new DefaultTableModel(rows, columns) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable table = new JTable(model);
table.setRowHeight(table.getFont().getSize() * 2);
add(new JScrollPane(table));
add(new JLabel(String.format("%s, %s, JRE %s (%s)",
System.getProperty("os.name"), System.getProperty("os.arch"),
System.getProperty("java.version"), Locale.getDefault().toString())),
BorderLayout.SOUTH);
}
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
TestTable panel = new TestTable();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
}
我的环境是Java 7 Win 7 x64,也使用Java 6和8进行了测试,它看起来一样。
答案 0 :(得分:4)
核心问题是JLabel
(DefaultTableCellRenderer
正在使用的)试图格式化HTML的方式,它允许HTML在它被包装时进行换行可用宽度是短的以容纳文本。这是JLabel
为什么这似乎只发生在选择单元格之后才是Swing的神奇之处......因为它应该&#34;应该&#34;一直在发生......
一种解决方案可能是使用布局管理器来防止(或阻止)JLabel
包裹在&#34;可用的&#34;宽点...但是,这需要提供您自己的TableCellRenderer
,例如......
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import sun.swing.DefaultLookup;
public class TestTable extends JPanel {
public TestTable() {
setLayout(new BorderLayout());
Object[][] rows = {
{"<html><font color=red>1 Lorem ipsum</font> dolor sit amet, "
+ "consectetur adipiscing elit. In lectus dolor</html>"},
{"<html><font color=green>2 Lorem ipsum</font> dolor sit amet, "
+ "consectetur adipiscing elit. In lectus dolor</html>"},
{"<html><font color=blue>3 Lorem ipsum</font> dolor sit amet, "
+ "consectetur adipiscing elit. In lectus dolor</html>"},
{"<html><font color=red>4 Lorem ipsum</font> dolor sit amet, "
+ "consectetur adipiscing elit. In lectus dolor</html>"},
{"<html><font color=green>5 Lorem ipsum</font> dolor sit amet, "
+ "consectetur adipiscing elit. In lectus dolor</html>"},};
Object[] columns = {"Column"};
DefaultTableModel model = new DefaultTableModel(rows, columns) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable table = new JTable(model);
table.setDefaultRenderer(Object.class, new HTMLRenderer());
table.setRowHeight(table.getFont().getSize() * 2);
add(new JScrollPane(table));
add(new JLabel(String.format("%s, %s, JRE %s (%s)",
System.getProperty("os.name"), System.getProperty("os.arch"),
System.getProperty("java.version"), Locale.getDefault().toString())),
BorderLayout.SOUTH);
}
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static class HTMLRenderer extends JPanel implements TableCellRenderer {
private JLabel label;
private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
protected static Border noFocusBorder = DEFAULT_NO_FOCUS_BORDER;
public HTMLRenderer() {
label = new DefaultTableCellRenderer();
// setOpaque(false);
setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
add(label);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (table == null) {
return this;
}
Color fg = null;
Color bg = null;
JTable.DropLocation dropLocation = table.getDropLocation();
if (dropLocation != null
&& !dropLocation.isInsertRow()
&& !dropLocation.isInsertColumn()
&& dropLocation.getRow() == row
&& dropLocation.getColumn() == column) {
fg = UIManager.getColor("Table.dropCellForeground");
bg = UIManager.getColor("Table.dropCellBackground");
isSelected = true;
}
if (isSelected) {
super.setForeground(fg == null ? table.getSelectionForeground()
: fg);
super.setBackground(bg == null ? table.getSelectionBackground()
: bg);
} else {
Color background = table.getBackground();
if (background == null || background instanceof javax.swing.plaf.UIResource) {
Color alternateColor = UIManager.getColor("Table.alternateRowColor");
if (alternateColor != null && row % 2 != 0) {
background = alternateColor;
}
}
super.setForeground(table.getForeground());
super.setBackground(background);
}
setFont(table.getFont());
if (hasFocus) {
Border border = null;
if (isSelected) {
border = UIManager.getBorder("Table.focusSelectedCellHighlightBorder");
}
if (border == null) {
border = UIManager.getBorder("Table.focusCellHighlightBorder");
}
setBorder(border);
if (!isSelected && table.isCellEditable(row, column)) {
Color col;
col = UIManager.getColor("Table.focusCellForeground");
if (col != null) {
super.setForeground(col);
}
col = UIManager.getColor("Table.focusCellBackground");
if (col != null) {
super.setBackground(col);
}
}
} else {
setBorder(getNoFocusBorder());
}
label.setText(value == null ? "" : value.toString());
return this;
}
protected Border getNoFocusBorder() {
Border border = UIManager.getBorder("Table.cellNoFocusBorder");
if (System.getSecurityManager() != null) {
if (border != null) return border;
return SAFE_NO_FOCUS_BORDER;
} else if (border != null) {
if (noFocusBorder == null || noFocusBorder == DEFAULT_NO_FOCUS_BORDER) {
return border;
}
}
return noFocusBorder;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
TestTable panel = new TestTable();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
}
<强>更新... 强>
我对JTable
和BasicTableUI
代码进行了很好的挖掘,TableCellRenderer
组件的大小已经过了&#34;&#34;根据单个单元格的要求,这意味着当呈现JLabel
时,它会自动包装文本而不考虑,为什么这会导致布局问题可能与默认{{1}的事实有关。 }} ...
更新了替代方案......
另一种选择可能是将verticalAlignment
设置为verticalAlignment
,JLabel.TOP
由DefaultTableCellRenderer
支持,例如......
JLabel
但这将取决于您的个人需求......
答案 1 :(得分:2)
这可能是由JLabel
垂直对齐引起的:
// Works for me (Java 1.7.0_65, Windows 7)
((JLabel) table.getDefaultRenderer(Object.class)).setVerticalAlignment(JLabel.TOP);
修改强>
这是我的测试代码:
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import javax.swing.*;
import javax.swing.table.*;
public class TestTable2 extends JPanel {
public TestTable2() {
super(new BorderLayout());
Object[][] rows = {
{
"<html><font color=red>1 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"
},
{
"<html><font color=green>2 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"
},
{
"<html><font color=blue>3 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"
},
{
"<html><font color=red>4 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"
},
{
"<html><font color=green>5 Lorem ipsum</font> dolor sit amet, " +
"consectetur adipiscing elit. In lectus dolor</html>"
},
};
Object[] columns = {"Column"};
DefaultTableModel model = new DefaultTableModel(rows, columns) {
@Override
public Class<?> getColumnClass(int column) {
return String.class;
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
final JTable table = new JTable(model);
//table.setRowHeight(table.getFont().getSize() * 2);
table.setRowHeight(20);
add(new JScrollPane(table));
final JRadioButton centerRadio = new JRadioButton("CENTER");
final JRadioButton topRadio = new JRadioButton("TOP");
final JRadioButton bottomRadio = new JRadioButton("BOTTOM");
ActionListener al = new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
TableCellRenderer r = table.getDefaultRenderer(String.class);
if (r instanceof JLabel) {
JLabel label = (JLabel) r;
if (topRadio.isSelected()) {
label.setVerticalAlignment(SwingConstants.TOP);
} else if (bottomRadio.isSelected()) {
label.setVerticalAlignment(SwingConstants.BOTTOM);
} else {
label.setVerticalAlignment(SwingConstants.CENTER);
}
table.repaint();
}
}
};
ButtonGroup bg = new ButtonGroup();
JPanel p = new JPanel();
for (JRadioButton b : Arrays.asList(centerRadio, topRadio, bottomRadio)) {
b.addActionListener(al);
bg.add(b);
p.add(b);
}
centerRadio.setSelected(true);
add(p, BorderLayout.SOUTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.add(new TestTable2());
frame.setSize(320, 240);
frame.setVisible(true);
}
});
}
}