Search code examples
javaswingjtablejtextareanimbus

How to remove/set JTextArea border in JTable cell in Nimbus look and feel


Edit: I have added a SSCCE code

I have extended JTextArea in TableCellRenderer because I wanted to achieve the Multiline text wrapping Mentioned in this SO question and that is working fine. Now I have ran into weird problem with Nimbus look and feel. Nimbus is adding a border with every cell as shown in the below picture. enter image description here

My Class:

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class TestJTableMultiline extends JFrame {
  /**
     * 
     */
    private static final long serialVersionUID = 1L;

public TestJTableMultiline() {
    super("Multi-Line Cell Example");
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    DefaultTableModel dm = new DefaultTableModel() {
      /**
         * 
         */
        private static final long serialVersionUID = 1L;
    public Class<String> getColumnClass(int columnIndex) {
        return String.class;
      }
      public boolean isCellEditable(int row, int column) {
        return false;
      }
    };
    dm.setDataVector(
        new Object[][]{
            {"A0, Line1\nA0, Line2\nA0, Line3", 
             "B0, Line1\nB0, Line2", 
             "C0, Line1"}, 
            {"A1, Line1", 
             "B1, Line1\nB1, Line2", 
             "C1, Line1"},
            {"A2, Line1", 
             "B2, Line1", 
             "C2, Line1"}
            }, 
            new Object[] {"A", "B", "C"});

    JTable table = new JTable(dm);
    table.setDefaultRenderer(String.class, new MultiLineTableCellRenderer());
    TableRowSorter<? extends TableModel> sort = new TableRowSorter<DefaultTableModel>(dm);
    table.setRowSorter(sort);
    JScrollPane scroll = new JScrollPane(table);
    getContentPane().add(scroll);
    setLocationByPlatform(true);
    setSize(400, 430);
    setVisible(true);
  }

  public static void main(String[] args) {
      try{
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    System.out.println("choosed nimbus");
                    break;
                }
            }
            }catch(Exception e){
                e.printStackTrace();
            }
    TestJTableMultiline frame = new TestJTableMultiline();   
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
  }
}

class MultiLineTableCellRenderer extends JTextArea 
implements TableCellRenderer {
/**
 * 
 */
private static final long serialVersionUID = 1L;
private List<List<Integer>> rowColHeight = new ArrayList<List<Integer>>();

public MultiLineTableCellRenderer() {
  setLineWrap(true);
  setWrapStyleWord(true);
  setOpaque(true);
}

@Override
public Component getTableCellRendererComponent(
    JTable table, Object value, boolean isSelected, boolean hasFocus,
    int row, int column) {
  if (isSelected) {
    setForeground(table.getSelectionForeground());
    setBackground(table.getSelectionBackground());
  } else {
    setForeground(table.getForeground());
    setBackground(table.getBackground());
  }
  setFont(table.getFont());
  if (hasFocus) {
    setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
    if (table.isCellEditable(row, column)) {
      setForeground(UIManager.getColor("Table.focusCellForeground"));
      setBackground(UIManager.getColor("Table.focusCellBackground"));
    }
  } else {
    setBorder(new EmptyBorder(1, 2, 1, 2));
  }
  if (value != null) {
    setText(value.toString());
  } else {
    setText("");
  }
  adjustRowHeight(table, row, column);
  return this;
}

/**
 * Calculate the new preferred height for a given row, and sets the height on the table.
 */
private void adjustRowHeight(JTable table, int row, int column) {
  //The trick to get this to work properly is to set the width of the column to the 
  //textarea. The reason for this is that getPreferredSize(), without a width tries 
  //to place all the text in one line. By setting the size with the with of the column, 
  //getPreferredSize() returnes the proper height which the row should have in
  //order to make room for the text.
  int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
  setSize(new Dimension(cWidth, 1000));
  int prefH = getPreferredSize().height;
  while (rowColHeight.size() <= row) {
    rowColHeight.add(new ArrayList<Integer>(column));
  }
  List<Integer> colHeights = rowColHeight.get(row);
  while (colHeights.size() <= column) {
    colHeights.add(0);
  }
  colHeights.set(column, prefH);
  int maxH = prefH;
  for (Integer colHeight : colHeights) {
    if (colHeight > maxH) {
      maxH = colHeight;
    }
  }
  if (table.getRowHeight(row) != maxH) {
    table.setRowHeight(row, maxH);
  }
}

}

What has been tried:

  1. I have tried setBorder(null) and setBorder(BorderFactory.createLineBorder(Color.black)) and various other color like forground and background color of the table.

  2. Then I looked at this SO question and tried that solution as well but I am not able to resolve the border problem. I have also tried removing setBorder call altogether but the results are same.

  3. I have also tried removing the cell border in jtable mentioned in this SO question

How can I resolve this border problem with Nimbus look and feel.

Note: I am getting nice view with other look and feel.


Solution

    • This might work in JDK 1.7.0 or later:

      MultiLineTableCellRenderer r = new MultiLineTableCellRenderer();
      UIDefaults d = new UIDefaults();
      d.put("TextArea.borderPainter", new Painter() {
        @Override public void paint(Graphics2D g, Object o, int w, int h) {}
      });
      r.putClientProperty("Nimbus.Overrides", d);
      r.putClientProperty("Nimbus.Overrides.InheritDefaults", false);
      
      table.setDefaultRenderer(String.class, r);
      

    enter image description here

    SSCCE

    import java.awt.*;
    import java.awt.event.*;
    import java.util.ArrayList;
    import java.util.List;
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.plaf.*;
    import javax.swing.table.*;
    import javax.swing.UIManager.LookAndFeelInfo;
    
    public class TestJTableMultiline2 {
      public JComponent makeUI() {
        String[] columnNames = {"A", "B", "C"};
        Object[][] data = {
          {"A0, Line1\nA0, Line2\nA0, Line3", "B0, Line1\nB0, Line2", "C0, Line1"},
          {"A1, Line1", "B1, Line1\nB1, Line2", "C1, Line1"},
          {"A2, Line1", "B2, Line1", "C2, Line1"}
        };
        DefaultTableModel model = new DefaultTableModel(data, columnNames) {
          @Override public Class<?> getColumnClass(int column) {
            return String.class;
          }
          @Override public boolean isCellEditable(int row, int column) {
            return false;
          }
        };
        JTable table = new JTable(model);
        table.setAutoCreateRowSorter(true);
    
        MultiLineTableCellRenderer r = new MultiLineTableCellRenderer();
        UIDefaults d = new UIDefaults();
        d.put("TextArea.borderPainter", new Painter() {
          @Override public void paint(Graphics2D g, Object o, int w, int h) {}
        });
        r.putClientProperty("Nimbus.Overrides", d);
        r.putClientProperty("Nimbus.Overrides.InheritDefaults", false);
    
        table.setDefaultRenderer(String.class, r);
        return new JScrollPane(table);
      }
      public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
          @Override public void run() {
            createAndShowGUI();
          }
        });
      }
      public static void createAndShowGUI() {
        try {
          for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
              UIManager.setLookAndFeel(info.getClassName());
              break;
            }
          }
        } catch(Exception e) {
          e.printStackTrace();
        }
        JFrame f = new JFrame("Multi-Line Cell Example 2");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.getContentPane().add(new TestJTableMultiline2().makeUI());
        f.setSize(320, 240);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
      }
    }
    
    class MultiLineTableCellRenderer extends JTextArea implements TableCellRenderer {
      private List<List<Integer>> rowColHeight = new ArrayList<List<Integer>>();
      //private Border fhb = UIManager.getBorder("Table.focusCellHighlightBorder");
      //private Border epb = BorderFactory.createEmptyBorder(2,5,2,5);
      public MultiLineTableCellRenderer() {
        super();
        setLineWrap(true);
        setWrapStyleWord(true);
        setOpaque(true);
        //System.out.println(fhb.getBorderInsets(this));
        //setBorder(epb);
      }
      @Override
      public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected,
        boolean hasFocus, int row, int column) {
        setFont(table.getFont());
        setText(value != null ? value.toString() : "");
        setMargin(new Insets(2,5,2,5));
        //setBorder(hasFocus ? fhb : epb);
        if (isSelected) {
          setForeground(table.getSelectionForeground());
          setBackground(table.getSelectionBackground());
        } else {
          setForeground(table.getForeground());
          setBackground(table.getBackground());
        }
        adjustRowHeight(table, row, column);
        return this;
      }
    
      /**
       * Calculate the new preferred height for a given row, and sets the height on the table.
       * http://blog.botunge.dk/post/2009/10/09/JTable-multiline-cell-renderer.aspx
       */
      private void adjustRowHeight(JTable table, int row, int column) {
        //The trick to get this to work properly is to set the width of the column to the
        //textarea. The reason for this is that getPreferredSize(), without a width tries
        //to place all the text in one line. By setting the size with the with of the column,
        //getPreferredSize() returnes the proper height which the row should have in
        //order to make room for the text.
        //int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
        int cWidth = table.getCellRect(row, column, false).width; //Ignore IntercellSpacing
        setSize(new Dimension(cWidth, 1000));
        int prefH = getPreferredSize().height;
        while (rowColHeight.size() <= row) {
          rowColHeight.add(new ArrayList<Integer>(column));
        }
        List<Integer> colHeights = rowColHeight.get(row);
        while (colHeights.size() <= column) {
          colHeights.add(0);
        }
        colHeights.set(column, prefH);
        int maxH = prefH;
        for (Integer colHeight : colHeights) {
          if (colHeight > maxH) {
            maxH = colHeight;
          }
        }
        if (table.getRowHeight(row) != maxH) {
          table.setRowHeight(row, maxH);
        }
      }
    }