Morgan
Morgan

Reputation: 927

Removing the border of a JTextField cell editor doesn't work under certain circumstances

This question is a follow up of this one due to issues that arose: Remove cell editor border in JTable (Windows LaF)

I am experiencing unexpected behaviour from the JTextArea taken from DefaultCellEditor#getComponent() when modifying its borders once it's already on the table. The borders simply don't visually change.

Even after calling:

textField.setBorder(BorderFactory.createCompoundBorder(null, BorderFactory.createEmptyBorder(1, 1, 1, 1)));

and right after that doing:

System.out.println(textField.getBorder());

the console shows javax.swing.border.CompoundBorder@6aff616 which means the border has changed internally. However, this isn't reflected visually and I'm very confused.

My test class:

public class TableStackOverflow extends javax.swing.JFrame {

    public TableStackOverflow() {
        initComponents();
        prepareTable();
    }

    public void prepareTable() {
        for (int i = 0; i < table.getColumnCount(); i++) {
            Class columnClass = table.getColumnClass(i);
            DefaultCellEditor defaultEditor = (DefaultCellEditor) table.getDefaultEditor(columnClass);
            if (defaultEditor.getComponent() instanceof JTextField) {
                JTextField textField = (JTextField) defaultEditor.getComponent();
                textField.setFont(new Font("Segoe UI", Font.PLAIN, 12));
                textField.setBorder(BorderFactory.createCompoundBorder(null, BorderFactory.createEmptyBorder(1, 1, 1, 1)));
                System.out.println(textField.getBorder());

            }
            defaultEditor.setClickCountToStart(1);
        }
    }

    private void initComponents() {    
        scrPane = new javax.swing.JScrollPane();
        table = new org.jdesktop.swingx.JXTable();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        table.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null},
                {null, null}
            },
            new String [] {
                "First", "Second"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        table.setSelectionBackground(new java.awt.Color(223, 238, 249));
        table.setSelectionForeground(new java.awt.Color(0, 0, 0));
        scrPane.setViewportView(table);

        getContentPane().add(scrPane, java.awt.BorderLayout.CENTER);

        pack();
    }                      

    public static void main(String args[]) {
        try {
            javax.swing.UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
        }

        java.awt.EventQueue.invokeLater(() -> {
            new TableStackOverflow().setVisible(true);
        });
    }

    private javax.swing.JScrollPane scrPane;
    private org.jdesktop.swingx.JXTable table;

And finally here is a nasty workaround that makes it work but isn't viable:

public void prepareTable() {
    for (int i = 0; i < table.getColumnCount(); i++) {
        DefaultCellEditor defaultEditor = (DefaultCellEditor) table.getDefaultEditor(table.getColumnClass(i));
        if (defaultEditor.getComponent() instanceof JTextField) {
            JTextField textField = new JTextField();
            textField.setFont(new Font("Segoe UI", Font.PLAIN, 12));
            textField.setBorder(BorderFactory.createCompoundBorder(null, BorderFactory.createEmptyBorder(1, 1, 1, 1)));
            defaultEditor = new DefaultCellEditor(textField);
            table.setDefaultEditor(table.getColumnClass(i), defaultEditor);
        }
        defaultEditor.setClickCountToStart(1);
    }
}

Finally, something much nicer that works (Thanks Camickr!)

table.addPropertyChangeListener((evt) -> {
    if ("tableCellEditor".equals(evt.getPropertyName())) {
        if (table.isEditing()) {
            JTextField jtextField = (JTextField) ((DefaultCellEditor) table.getCellEditor()).getComponent();
            jtextField.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        }
    }
});

Upvotes: 1

Views: 387

Answers (1)

camickr
camickr

Reputation: 324207

DefaultCellEditor defaultEditor = (DefaultCellEditor) table.getDefaultEditor(columnClass);
System.out.println(defaultEditor.getClass());

Add the above statement to your code and you will see that the default editor is the GenericEditor which is an inner class of the JTable.

This editor adds extra functionality for usage in a JTable.

One of the things it does is manage the Border. If an error is found a "red border" is displayed in the editor. Otherwise the border is set the the default "black border".

And finally here is a nasty workaround that makes it work but isn't viable:

You either need to use the DefaultCellEditor or create a custom editor.

You might be able to extend the JTable.GenericEditor class and override the getTableCellEditor(...) method. This is the method that resets the Border to the default black border.

Or maybe you can override the getCellEditor(...) method to the table to get the editor and then remove the border.

Another approach would be to add a PropertyChangeListener to the table. Then whenever a cell is edited you will get an event so you can get the active editor and remove its border:

@Override
public void propertyChange(PropertyChangeEvent e)
{
    //  A cell has started/stopped editing

    if ("tableCellEditor".equals(e.getPropertyName()))
    {
        if (table.isEditing())
            // add your code here
    }
}

Also, why would you use a CompoundBorder to create an EmptyBorder?

Upvotes: 2

Related Questions