在JTable列中使用JCheckBox作为编辑器时,我想忽略TableCell中CheckBox左右空间中的鼠标单击。

我在Oracle论坛上找到了2011年的讨论,但是问题在那里并没有解决:https://community.oracle.com/thread/2183210

到目前为止,这是我已经意识到的hack,有趣的部分始于class CheckBoxEditor:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import javax.swing.DefaultCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

/**
 * Trying to set the Checkbox only if clicked directly on the box of the CheckBox. And ignore clicks on the
 * remaining space of the TableCell.
 *
 * @author bobndrew
 */
public class JustCheckOnCheckboxTable extends JPanel
{
  private static final int        CHECK_COL = 1;
  private static final Object[][] DATA      = { { "One", Boolean.TRUE }, { "Two", Boolean.FALSE },
      { "Three", Boolean.TRUE }, { "Four", Boolean.FALSE }, { "Five", Boolean.TRUE },
      { "Six", Boolean.FALSE }, { "Seven", Boolean.TRUE }, { "Eight", Boolean.FALSE },
      { "Nine", Boolean.TRUE }, { "Ten", Boolean.FALSE } };
  private static final String[]   COLUMNS   = { "Number", "CheckBox" };
  private final DataModel         dataModel = new DataModel( DATA, COLUMNS );
  private final JTable            table     = new JTable( dataModel );

  public JustCheckOnCheckboxTable()
  {
    super( new BorderLayout() );
    this.add( new JScrollPane( table ) );
    table.setRowHeight( table.getRowHeight() * 2 );
    table.setPreferredScrollableViewportSize( new Dimension( 250, 400 ) );
    TableColumn checkboxColumn = table.getColumnModel().getColumn( 1 );
    checkboxColumn.setCellEditor( new CheckBoxEditor() );
  }

  private class DataModel extends DefaultTableModel
  {
    public DataModel( Object[][] data, Object[] columnNames )
    {
      super( data, columnNames );
    }

    @Override
    public Class<?> getColumnClass( int columnIndex )
    {
      if ( columnIndex == 1 )
      {
        return getValueAt( 0, CHECK_COL ).getClass();
      }
      return super.getColumnClass( columnIndex );
    }
  }


  class CheckBoxEditor extends DefaultCellEditor
  {
    private final JCheckBox checkBox;

    public CheckBoxEditor()
    {
      super( new JCheckBox() );
      checkBox = (JCheckBox) getComponent();
      checkBox.setHorizontalAlignment( JCheckBox.CENTER );
      System.out.println( "the checkbox has no size:   " + checkBox.getSize() );
    }

    @Override
    public boolean shouldSelectCell( final EventObject anEvent )
    {
      System.out.println( "\nthe checkbox fills the TableCell:  " + checkBox.getSize() );
      //Throws NullPointerException:      System.out.println( checkBox.getIcon().getIconWidth() );
      System.out.println( "always JTable :-(   " + anEvent.getSource() );

      MouseEvent ev =
          SwingUtilities.convertMouseEvent( ((ComponentEvent) anEvent).getComponent(), (MouseEvent) anEvent,
          getComponent() );
      System.out.println( "Position clicked in TableCell:   " + ev.getPoint() );
      System.out.println( "always JCheckBox :-(   " + getComponent().getComponentAt( ev.getPoint() ) );

      Point middleOfTableCell = new Point( checkBox.getWidth() / 2, checkBox.getHeight() / 2 );
      System.out.println( "middleOfTableCell: " + middleOfTableCell );

      Dimension preferredSizeOfCheckBox = checkBox.getPreferredSize();

      int halfWidthOfClickArea = (int) (preferredSizeOfCheckBox.getWidth() / 2);
      int halfHeightOfClickArea = (int) (preferredSizeOfCheckBox.getHeight() / 2);

      if ( (middleOfTableCell.getX() - halfWidthOfClickArea > ev.getX() || middleOfTableCell.getX() + halfWidthOfClickArea < ev.getX())
        || (middleOfTableCell.getY() - halfHeightOfClickArea > ev.getY() || middleOfTableCell.getY() + halfHeightOfClickArea < ev.getY()) )
      {
        stopCellEditing();
      }

      return super.shouldSelectCell( anEvent );
    }
  }


  private static void createAndShowUI()
  {
    JFrame frame = new JFrame( "Direct click on CheckBox" );
    frame.add( new JustCheckOnCheckboxTable() );
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.pack();
    frame.setLocationRelativeTo( null );
    frame.setVisible( true );
  }

  public static void main( String[] args )
  {
    java.awt.EventQueue.invokeLater( new Runnable()
    {
      @Override
      public void run()
      {
        createAndShowUI();
      }
    } );
  }
}

我喜欢这个解决方案:
  • 所有TableCell行为均正确:选择,MouseOver,EditModes,...

  • 我不喜欢的是:
  • JCheckBox的硬编码大小(int halfWidthOfClickArea)
  • 在哪里可以获得未绘制(paint)的组件的尺寸?

  • 还是有更好的方法来实现此Table和CheckBox行为?

    编辑:

    我按照camickr的建议更改了源代码,并为具有较高RowHeights的表添加了垂直的垂直区域。
    但是到目前为止,我忘记提到我的问题的主要原因... ;-)
    我在stopCellEditing()方法中调用shouldSelectCell(..)

    除了确定单元格之外,还可以决定更多其他内容吗?

    最佳答案


      System.out.println(checkBox.getPreferredSize() );
    

    10-08 14:26