JTable 单元格中的 JButtons 在行选择时改变形状

JButtons in JTable cell change shape on row selection

我每行有一个单元格(列)table,单元格是一个面板,上面有 2 个按钮。 TableCellRenderer 和 TableCellEditor 已正确实现以允许呈现和编辑单元格。但是当我点击行甚至按钮时,按钮的形状会改变。看起来按钮的大小缩小以适合按钮文本。请参阅所附图片。

如果我设置了按钮的首选大小,那么大小似乎没有改变,但按钮文本向右移动。下面是 table 单元面板的代码。 运行从如下所示的相同 class 中的主面板打开此面板不会显示此行为。此行为仅在将此面板用作单列 table 行中的单元格时可见。不知道哪个 属性 哪个 set/change.

public class PartitionsTableRowPanel extends javax.swing.JPanel {

    /**
     * Creates new form PartitionsTableRowPanel
     */
    public PartitionsTableRowPanel() {
        initComponents();
        postInitAdjustments();
    }

    private void postInitAdjustments() {
//        infoButton.putClientProperty("JButton.buttonType", "roundRect");
//        scanButton.putClientProperty("JButton.buttonType", "roundRect");
//        
//        infoButton.addActionListener((e) -> {
//            log.info("info clicked");
//        });
        
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();

        setPreferredSize(new java.awt.Dimension(1000, 59));
        setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());

        jButton1.setText("Info");
        add(jButton1, new org.netbeans.lib.awtextra.AbsoluteConstraints(10, 20, -1, -1));

        jButton2.setText("jButton2");
        add(jButton2, new org.netbeans.lib.awtextra.AbsoluteConstraints(90, 20, -1, -1));
    }// </editor-fold>                        
    
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            FlatDarkLaf.install();
            JFrame f = new JFrame("TestProgressBar");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setContentPane(new PartitionsTableRowPanel());
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });

    }

    // Variables declaration - do not modify                     
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    // End of variables declaration                   
}

下面是我的主要 JFrame 的样子:

public class MainAppWindow extends javax.swing.JFrame {

    /**
     * Creates new form MainWindow1
     */
    public MainAppWindow() {
        initComponents();
        postInitAdjustments();
    }

    private void postInitAdjustments() {
        
    }
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        northPanel = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        partitionDetailsTable = new javax.swing.JTable();
        centerPanel = new javax.swing.JPanel();
        messageDetailsTextPaneScrollPane = new javax.swing.JScrollPane();
        messageDetailsTextPane = new javax.swing.JTextPane();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Sentry Disk Helper");
        setName("mainFrame"); // NOI18N
        setPreferredSize(new java.awt.Dimension(984, 606));

        northPanel.setName("northPanel"); // NOI18N

        partitionDetailsTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
        partitionDetailsTable.setFocusable(false);
        partitionDetailsTable.setName("partitionsTable"); // NOI18N
        jScrollPane1.setViewportView(partitionDetailsTable);

        javax.swing.GroupLayout northPanelLayout = new javax.swing.GroupLayout(northPanel);
        northPanel.setLayout(northPanelLayout);
        northPanelLayout.setHorizontalGroup(
            northPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(northPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 1048, Short.MAX_VALUE)
                .addContainerGap())
        );
        northPanelLayout.setVerticalGroup(
            northPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(northPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 238, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        getContentPane().add(northPanel, java.awt.BorderLayout.NORTH);

        centerPanel.setBounds(new java.awt.Rectangle(2, 4, 0, 0));
        centerPanel.setName("centerPanel"); // NOI18N

        messageDetailsTextPane.setFocusable(false);
        messageDetailsTextPaneScrollPane.setViewportView(messageDetailsTextPane);

        javax.swing.GroupLayout centerPanelLayout = new javax.swing.GroupLayout(centerPanel);
        centerPanel.setLayout(centerPanelLayout);
        centerPanelLayout.setHorizontalGroup(
            centerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(centerPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(messageDetailsTextPaneScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1048, Short.MAX_VALUE)
                .addContainerGap())
        );
        centerPanelLayout.setVerticalGroup(
            centerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, centerPanelLayout.createSequentialGroup()
                .addComponent(messageDetailsTextPaneScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE)
                .addContainerGap())
        );

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

        getAccessibleContext().setAccessibleName("Sentery Disk Helper");

        setSize(new java.awt.Dimension(984, 628));
        setLocationRelativeTo(null);
    }// </editor-fold>                        

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MainAppWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MainAppWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MainAppWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MainAppWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                 //FlatIntelliJLaf.install();
                new MainAppWindow().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JPanel centerPanel;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextPane messageDetailsTextPane;
    private javax.swing.JScrollPane messageDetailsTextPaneScrollPane;
    private javax.swing.JPanel northPanel;
    private javax.swing.JTable partitionDetailsTable;
    // End of variables declaration                   
}

添加示例代码以帮助重现问题。 运行 主要在NewJFrame1 class 看问题。取消注释并 运行 TableRowPanel1 private class 中的 main 以查看默认的正确工作。如果我直接在 JFrame 中使用 table 单元格面板组件,它工作正常。但是通过编辑器用作 table 单元格的相同组件显示了问题。请真的需要帮助。:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.swing.test.demo.ui;

import com.formdev.flatlaf.FlatDarkLaf;
import com.swing.test.demo.ui.components.TableRowPanel;
import java.awt.Component;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractCellEditor;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

public class NewJFrame1 extends javax.swing.JFrame {

    /**
     * Creates new form NewJFrame1
     */
    public NewJFrame1() {
        initComponents();
        postInit();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
        jScrollPane1.setViewportView(jTable1);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 481, Short.MAX_VALUE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 337, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
//        try {
//            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
//                if ("Nimbus".equals(info.getName())) {
//                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
//                    break;
//                }
//            }
//        } catch (ClassNotFoundException ex) {
//            java.util.logging.Logger.getLogger(NewJFrame1.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
//        } catch (InstantiationException ex) {
//            java.util.logging.Logger.getLogger(NewJFrame1.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
//        } catch (IllegalAccessException ex) {
//            java.util.logging.Logger.getLogger(NewJFrame1.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
//        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
//            java.util.logging.Logger.getLogger(NewJFrame1.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
//        }
        //</editor-fold>
        FlatDarkLaf.setup();
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                NewJFrame1 newJFrame1 = new NewJFrame1();
                newJFrame1.setVisible(true);

            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;
    // End of variables declaration                   

    private void postInit() {
        List<Data> modelData = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            Data data = new Data();
            data.value = "Data_" + i;
            modelData.add(data);
        }
        jTable1.setDefaultRenderer(Data.class, new MyCellRenderer());
        jTable1.setDefaultEditor(Data.class, new MyCellEditor());
        jTable1.setModel(new MyTableModel(modelData));
        jTable1.setRowHeight(52);
        jTable1.revalidate();
        //
    }

    private class Data {

        String value;
    }

    private class MyTableModel extends AbstractTableModel {

        private List<Data> data;

        public MyTableModel(List<Data> data) {
            this.data = data;
        }

        @Override
        public int getRowCount() {
            return data.size();
        }

        @Override
        public int getColumnCount() {
            return 1;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return data.get(rowIndex);
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return Data.class;
        }

    }

    private class MyCellRenderer implements TableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            System.out.println("fff");
            return new TableRowPanel1();
        }

    }

    private class MyCellEditor extends AbstractCellEditor implements TableCellEditor {

        @Override
        public Object getCellEditorValue() {
            return null;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            System.out.println("ddd");
            return new TableRowPanel1();
        }

    }

    private class TableRowPanel1 extends javax.swing.JPanel {

        /**
         * Creates new form TableRowPanel
         */
        public TableRowPanel1() {
            initComponents();
        }

        /**
         * This method is called from within the constructor to initialize the
         * form. WARNING: Do NOT modify this code. The content of this method is
         * always regenerated by the Form Editor.
         */
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
        private void initComponents() {

            jButton1 = new javax.swing.JButton();
            jButton2 = new javax.swing.JButton();

            jButton1.setText("jButton1");

            jButton2.setText("jButton2");

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
            this.setLayout(layout);
            layout.setHorizontalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                    .addContainerGap()
                                    .addComponent(jButton1)
                                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                    .addComponent(jButton2)
                                    .addContainerGap(250, Short.MAX_VALUE))
            );
            layout.setVerticalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                    .addGap(14, 14, 14)
                                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                            .addComponent(jButton1)
                                            .addComponent(jButton2))
                                    .addContainerGap(14, Short.MAX_VALUE))
            );
        }// </editor-fold>                        

        // Variables declaration - do not modify                     
        private javax.swing.JButton jButton1;
        private javax.swing.JButton jButton2;
        // End of variables declaration                   

//        public static void main(String[] args) {
//            EventQueue.invokeLater(() -> {
//                FlatDarkLaf.setup();
//                JFrame f = new JFrame("TestProgressBar");
//                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//                f.setContentPane(new TableRowPanel());
//                f.pack();
//                f.setLocationRelativeTo(null);
//                f.setVisible(true);
//            });
//
//        }
    }

}

根据源代码,根据FlatBorder规范,table单元格编辑器的边框插入为0。

com/formdev/flatlaf/ui/FlatBorder.java

protected boolean isCellEditor( Component c ) {
    return FlatUIUtils.isCellEditor( c );
}

@Override
public Insets getBorderInsets( Component c, Insets insets ) {
    // ...

    if( isCellEditor( c ) ) {
        // remove top and bottom insets if used as cell editor
        insets.top = insets.bottom = 0;
        // ...

com/formdev/flatlaf/ui/FlatUIUtils.java

public static boolean isCellEditor( Component c ) {
    // check whether used in cell editor (check 3 levels up)

Any ideas about how to rectify this?

将单元格编辑器面板层次结构设为三级或更多级。

class TableRowPanel1 extends JPanel {
  protected final JButton jButton1 = new JButton("jButton1");
  protected final JButton jButton2 = new JButton("jButton2");

  public TableRowPanel1() {
    super(new BorderLayout());
    JPanel p2 = new JPanel(new FlowLayout(FlowLayout.LEADING));
    p2.add(jButton1);
    p2.add(jButton2);
    JPanel p1 = new JPanel(new BorderLayout());
    p1.add(p2);
    add(p1);
  }
}

或覆盖 FlatBorder#isCellEditor(Component c) 方法以始终 return false.

class TableRowPanel1 extends JPanel {
  protected final JButton jButton1 = new JButton("jButton1");
  protected final JButton jButton2 = new JButton("jButton2");

  public TableRowPanel1() {
    super(new FlowLayout(FlowLayout.LEADING));
    FlatBorder border = new FlatButtonBorder() {
      @Override protected boolean isCellEditor(Component c) {
        return false;
      }
    };
    jButton1.setBorder(border);
    jButton2.setBorder(border);
    add(jButton1);
    add(jButton2);
  }
}

NewJFrame2.java

import com.formdev.flatlaf.*;
import com.formdev.flatlaf.ui.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;

public class NewJFrame2 {
  public Component makeUI() {
    List<Data> modelData = new ArrayList<>();
    for (int i = 0; i < 4; i++) {
      Data data = new Data();
      data.value = "Data_" + i;
      modelData.add(data);
    }
    JTable table = new JTable(new MyTableModel(modelData));
    table.setDefaultRenderer(Data.class, new MyCellRenderer());
    table.setDefaultEditor(Data.class, new MyCellEditor());
    table.setRowHeight(52);

    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    p.add(new TableRowPanel1(), BorderLayout.SOUTH);
    return p;
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      FlatDarkLaf.setup();
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new NewJFrame2().makeUI());
      f.pack();
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class Data {
  String value;
}

class MyTableModel extends AbstractTableModel {
  private List<Data> data;

  public MyTableModel(List<Data> data) {
    this.data = data;
  }

  @Override
  public int getRowCount() {
    return data.size();
  }

  @Override
  public int getColumnCount() {
    return 1;
  }

  @Override
  public Object getValueAt(int rowIndex, int columnIndex) {
    return data.get(rowIndex);
  }

  @Override
  public boolean isCellEditable(int rowIndex, int columnIndex) {
    return true;
  }

  @Override
  public Class<?> getColumnClass(int columnIndex) {
    return Data.class;
  }
}

class MyCellRenderer implements TableCellRenderer {
  protected final TableRowPanel1 renderer = new TableRowPanel1();

  @Override
  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    return renderer;
  }
}

class MyCellEditor extends AbstractCellEditor implements TableCellEditor {
  protected final TableRowPanel1 editor = new TableRowPanel1();

  @Override
  public Object getCellEditorValue() {
    return null;
  }

  @Override
  public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    return editor;
  }
}

class TableRowPanel1 extends JPanel {
  protected final JButton jButton1 = new JButton("jButton1");
  protected final JButton jButton2 = new JButton("jButton2");

  public TableRowPanel1() {
    super(new BorderLayout());
    JPanel p2 = new JPanel(new FlowLayout(FlowLayout.LEADING));
    p2.add(jButton1);
    p2.add(jButton2);
    JPanel p1 = new JPanel(new BorderLayout());
    p1.add(p2);
    add(p1);
  }
}

// class TableRowPanel1 extends JPanel {
//   protected final JButton jButton1 = new JButton("jButton1");
//   protected final JButton jButton2 = new JButton("jButton2");
// 
//   public TableRowPanel1() {
//     super(new FlowLayout(FlowLayout.LEADING));
//     FlatBorder border = new FlatButtonBorder() {
//       @Override protected boolean isCellEditor(Component c) {
//         return false;
//       }
//     };
//     jButton1.setBorder(border);
//     jButton2.setBorder(border);
//     add(jButton1);
//     add(jButton2);
//   }
// }