Java HTML字符串呈现为多行的表格单元格不一致

Java HTML字符串呈现为多行的表格单元格不一致,java,swing,jtable,jlabel,tablecellrenderer,Java,Swing,Jtable,Jlabel,Tablecellrenderer,我的表中一列的单元格是HTML字符串。HTML用于提供一些颜色指示。通常,列的宽度足以包含整个字符串。但当这还不够时,字符串就会在单词边界上被很好地切割。这是理想的行为。使用默认的单元渲染器 我注意到,偶尔,与表的一些交互会触发渲染器来包装字符串。据我所知,包装HTML字符串是JLabel的正常行为,DefaultTableCellRenderer从中派生。目前尚不清楚的是,为什么这种行为如此不一致,是什么触发了它的偏差。JLabel来回跳跃的原因是什么,好像它一直在被重新测量?有关示例,请参见

我的表中一列的单元格是HTML字符串。HTML用于提供一些颜色指示。通常,列的宽度足以包含整个字符串。但当这还不够时,字符串就会在单词边界上被很好地切割。这是理想的行为。使用默认的单元渲染器

我注意到,偶尔,与表的一些交互会触发渲染器来包装字符串。据我所知,包装HTML字符串是
JLabel
的正常行为,
DefaultTableCellRenderer
从中派生。目前尚不清楚的是,为什么这种行为如此不一致,是什么触发了它的偏差。
JLabel
来回跳跃的原因是什么,好像它一直在被重新测量?有关示例,请参见附图

为了解决这个问题,我可以将
添加到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.awt.BorderLayout;
导入java.awt.Dimension;
导入java.util.Locale;
导入javax.swing.JFrame;
导入javax.swing.JLabel;
导入javax.swing.JPanel;
导入javax.swing.JScrollPane;
导入javax.swing.JTable;
导入javax.swing.SwingUtilities;
导入javax.swing.table.DefaultTableModel;
公共类TestTable扩展了JPanel{
公共测试表(){
setLayout(新的BorderLayout());
对象[][]行={
{“1 Lorem ipsum door sit amet,”+
“在莱克托斯·多洛的《献祭精英》”,
{“2 Lorem ipsum door sit amet,”+
“在莱克托斯·多洛的《献祭精英》”,
{“3 Lorem ipsum door sit amet,”+
“在莱克托斯·多洛的《献祭精英》”,
{“4 Lorem ipsum door sit amet,”+
“在莱克托斯·多洛的《献祭精英》”,
{“5 Lorem ipsum door sit amet,”+
“在莱克托斯·多洛的《献祭精英》”,
};
对象[]列={“列”};
DefaultTableModel=新的DefaultTableModel(行、列){
@凌驾
公共布尔值可编辑(int行,int列){
返回false;
}
};
JTable table=新的JTable(模型);
table.setRowHeight(table.getFont().getSize()*2);
添加(新JScrollPane(表));
添加(新的JLabel(String.format(“%s,%s,JRE%s(%s)”),
System.getProperty(“os.name”)、System.getProperty(“os.arch”),
System.getProperty(“java.version”)、Locale.getDefault().toString()),
边界布局(南部);
}
公共维度getPreferredSize(){
返回新维度(300200);
}
公共静态void main(字符串[]args){
SwingUtilities.invokeLater(新的Runnable(){
public void run(){
JFrame=新JFrame(“测试”);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(真);
TestTable面板=新的TestTable();
框架。添加(面板);
frame.pack();
frame.setVisible(true);
}
});
}
}

我的环境是Java7Win7x64,也用Java6和Java8进行了测试,看起来是一样的

这可能是由
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);
      }
    });
  }
}
import java.awt.*;
导入java.awt.event.*;
导入java.util.array;
导入javax.swing.*;
导入javax.swing.table.*;
公共类TestTable2扩展了JPanel{
公共测试表2(){
超级(新边框布局());
对象[][]行={
{
“1 Lorem ipsum dolor sit amet,”+
“在莱克托斯·多洛的《献祭精英》
},
{
“2 Lorem ipsum dolor sit amet,”+
“在莱克托斯·多洛的《献祭精英》
},
{
“3 Lorem ipsum dolor sit amet,”+
“在莱克托斯·多洛的《献祭精英》
},
{
“4 Lorem ipsum dolor sit amet,”+
“在莱克托斯·多洛的《献祭精英》
},
{
“5 Lorem ipsum dolor sit amet,”+
“在莱克托斯·多洛的《献祭精英》
},
};
对象[]列={“列”};
DefaultTableModel=新的DefaultTableModel(行、列){
@凌驾
公共类getColumnClass(int列){
返回字符串.class;
}
@凌驾
公共布尔值可编辑(int行,int列){
返回false;
}
};
最终JTable表格=新JTable(模型);
//table.setRowHeight(table.getFont().getSize()*2);
表2.设置行高(20);
添加(新JScrollPane(表));
最终JRadioButton centerRadio=新JRadioButton(“中心”);
最终JRadioButton topRadio=新JRadioButton(“TOP”);
最终JRadioButton bottomRadio=新JRadioButton(“BOTTOM”);
ActionListener al=新的ActionListener(){
@覆盖已执行的公共无效操作(ActionEvent e){
TableCellRenderer r=table.getDefaultRenderer(String.class);
if(JLabel的r实例){
JLabel标签=(JLabel)r;
if(topRadio.isSelected()){
标签.设置垂直对齐(SwingConstants.顶部);
}else if(bottomRadio.isSelected()){
标签。设置垂直对齐(旋转恒量。底部);
}否则{
标签。设置垂直对齐(旋转恒量中心);
}
表1.repaint();
}
}
};
ButtonGroup bg=新建ButtonGroup();
JPanel p=新J
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);
            }
        });
    }
}
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;

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 DefaultTableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            setVerticalAlignment(JLabel.TOP);
            return comp;
        }

    }

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