Table cells with HTML strings inconsistently rendered as multiline

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-04 05:40:03

The core problem is the way that the JLabel (which the DefaultTableCellRenderer is using) is trying to format the HTML, it's allowing the HTML to wrap when it's available width is to short to accommodate the text. This is the default behaviour for JLabel

Why this seems to only happen after the cell is selected is one of those wonderful mysterious of Swing...cause it "should" be happening all the time...

One solution might be to use a layout manager which will prevent (or discourage) the JLabel from wrapping at the "available" width point...This, however, would require to provide your own TableCellRenderer, for example...

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

Updated...

I had a nice dig through the JTable and BasicTableUI code and the TableCellRenderer component is been "sized" to the requirements of the individual cell, meaning that when the JLabel is rendered, it is automatically wrapping the text without consideration, why this then causes issues with the layout may have to do with the fact that the default verticalAlignment...

Updated with alternative...

Another alternative might be to set the verticalAlignment to JLabel.TOP of a DefaultTableCellRenderer, which is backed by a JLabel, for example...

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

But this will come down to your individual needs...

That may have been caused by the JLabel vertical alignment:

// Works for me (Java 1.7.0_65, Windows 7)
((JLabel) table.getDefaultRenderer(Object.class)).setVerticalAlignment(JLabel.TOP);

EDIT

Here is my testing code:

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);
      }
    });
  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!