在OS X Lion上使用Java 7中的JTables对setValueAt进行虚假调用?

在OS X Lion上使用Java 7中的JTables对setValueAt进行虚假调用?,java,swing,osx-lion,java-7,Java,Swing,Osx Lion,Java 7,升级到Lion和Java7之后,我遇到了JTables的问题。当我使用箭头键移动选择时,其调用setValueAt(),将空字符串作为编辑值 为了测试这一点,我创建了一个简单的JFrame,其中包含一个表,并将下面的类设置为它的模型 public class SpyModel extends AbstractTableModel { public int getColumnCount() { return 5; } public int getRowCount() { retur

升级到Lion和Java7之后,我遇到了JTables的问题。当我使用箭头键移动选择时,其调用
setValueAt()
,将空字符串作为编辑值

为了测试这一点,我创建了一个简单的JFrame,其中包含一个表,并将下面的类设置为它的模型

public class SpyModel extends AbstractTableModel {
    public int getColumnCount() { return 5; }
    public int getRowCount() { return 5; }
    public Object getValueAt(int rowIndex, int columnIndex) { return ""; }
    public boolean isCellEditable(int rowIndex, int columnIndex) { return true; }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        System.out.println(aValue == null ? "null" : "\"" + aValue + "\"");
    }
}
当我在Java6下运行它时,然后使用箭头键在其中移动。它很好用。e、 g

$ java -version
java version "1.6.0_33"
Java(TM) SE Runtime Environment (build 1.6.0_33-b03-424-11M3720)
Java HotSpot(TM) 64-Bit Server VM (build 20.8-b03-424, mixed mode)
$ java -jar JavaApplication5.jar 
但是,当我在Java 7(在Lion上)下运行它,并用箭头键移动选择时,它最终会用空字符串调用
setValueAt()

e、 g


我已经找过虫子了,但什么也没找到。这是一个已知的问题吗

在使用该表示例时,似乎存在不止一个bug

我使用了下面的SSCCE,它在JDK1.6下按预期工作

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class TableBugDemo {
  public static void main( String[] args ) {
    JFrame frame = new JFrame( "TestFrame" );
    final JTable table = new JTable( new SpyModel() );
    table.getSelectionModel().addListSelectionListener( new ListSelectionListener() {
      @Override
      public void valueChanged( ListSelectionEvent e ) {
        Thread.dumpStack();
        System.out.println(table.getSelectedRow());
      }
    } );
    frame.add( table );
    frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
    frame.pack();
    frame.setVisible( true );
  }

  public static class SpyModel extends DefaultTableModel{
    public SpyModel() {
      super( new String[][]{
          new String[]{ "row1-1", "row1-2", "row1-3"},
          new String[]{ "row2-1", "row2-2", "row2-3"},
          new String[]{ "row3-1", "row3-2", "row3-3"},
          new String[]{ "row4-1", "row4-2", "row4-3"},
      }, new String[]{"col1", "col2", "col3"});
    }

    @Override
    public void setValueAt( Object aValue, int row, int column ) {
      System.out.println( "TableBugDemo$SpyModel.setValueAt" );
      Thread.dumpStack();
      super.setValueAt( aValue, row, column );
    }

    @Override
    public boolean isCellEditable( int row, int column ) {
      return false;
    }
  }
}
但是,在JDK1.7下:

  • 我看到在使表格可编辑时调用了
    setValueAt
    。但是,不是使用空字符串,而是使用my
    TableModel
    中包含的实际值。这意味着我的数据没有任何更改。唯一恼人的是,在导航过程中,我的表不断更新。解决方法当然是在值根本没有更新时,通过快速退出路径调整
    setValueAt
    方法,例如添加

    if ( ( aValue != null && aValue.equals( getValueAt( row, column ) ) ) ||
         ( aValue == null && getValueAt( row, column ) == null ) ){
      return;
    }
    
  • 使用向上和向下箭头导航可使选择一次跳过两行。Stacktraces显示选择的变化源于
    BasicTableUI#Actions
    类(这很有意义,因为这是放置在动作图中的动作)。奇怪的是,按下一个键,这个动作会触发两次。这已经解释了为什么选择一次跳两行。进一步的调试显示,对于一次按我的箭头键,我收到了两个不同的
    键被按下的事件。据我所知,这些事件与
    EventQueue
    上的事件类似,与
    JTable
    无关。为了确保这一点,我创建了一个小型SSCCE,其中不包括
    JTable

     import javax.swing.JFrame;
     import javax.swing.WindowConstants;
     import java.awt.AWTEvent;
     import java.awt.EventQueue;
     import java.awt.Toolkit;
     import java.awt.event.AWTEventListener;
     import java.awt.event.KeyEvent;
    
     public class KeyEventBugDemo {
       public static void main( String[] args ) {
         EventQueue.invokeLater(new Runnable() {
           @Override
           public void run() {
             JFrame testframe = new JFrame( "testframe" );
             testframe.setSize( 300,300 );
             testframe.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
             testframe.setVisible( true );
           }
         } );
         Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener() {
           @Override
           public void eventDispatched( AWTEvent event ) {
             if (event instanceof KeyEvent ){
               KeyEvent keyevent = ( KeyEvent ) event;
               System.out.println( "keyevent.getKeyCode() = " + keyevent.getKeyCode() );
               System.out.println( "ID = " + System.identityHashCode( keyevent ) );
               System.out.println( "keyevent = " + keyevent );
             }
           }
         }, AWTEvent.KEY_EVENT_MASK );
       }
     }
    
将焦点放在帧上,然后按下向下箭头,会产生以下输出(去掉
toString
的输出以保持可读性)

在这里,您可以清楚地看到按下两个
事件会混淆
JTable
。使用常规字符键时不会发生这种情况

keyevent.getKeyCode() = 65
ID = 1023134153
keyevent = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=65,keyText=A,
ID = 914147942
keyevent = java.awt.event.KeyEvent[KEY_TYPED,keyCode=0,keyText=Unknown 
keyevent.getKeyCode() = 65
ID = 986450556
keyevent = java.awt.event.KeyEvent[KEY_RELEASED,keyCode=65,keyText=A,keyChar='a',
查看
KeyEvent
类中的javadoc:

键入的密钥(仅当可以生成有效的Unicode字符时才生成。)

按箭头时,我不会键入
事件,但按两次
键会触发,我认为这是一个错误(稍后将为此记录错误报告)。一个解决办法可能是拦截这样一个事件,而不是通过链传递它,但这听起来像是一个丑陋的黑客

编辑

另一件奇怪的事。如果将以下行添加到代码段中

table.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).
  put( KeyStroke.getKeyStroke( 'a' ), "selectNextRow" );

您可以使用
a
跳到下一行(与默认情况下使用向下箭头触发的操作相同)。由于按下
a
时事件顺序正确,因此似乎也不会调用
setValueAt
方法。这使我认为按下两个
事件会以某种方式启动编辑…

解决方法是使用:

putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);

但在这种情况下,您将无法通过直接键入在单元格中开始编辑。您必须使用鼠标开始编辑。我还向Oracle发布了一个bug,但它也不可用。。。似乎他们的系统有问题。

除了更新,我仍然看到1.7.0(40)的问题,因为我用光标键在我的JTable单元格中移动,它会导致进入的每个字段都被清空

添加:

table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE)
停止该问题的发生,但我必须按“回车”键开始编辑

相反,在Jtables模型开始时,检查空字符串并返回将解决此问题

i、 e

但这确实意味着,如果用户确实想要清空该字段,那么更改将被拒绝。在我自己的应用程序中,这并不是一个问题,因为我有一个单独的deleteField()操作供他们使用

这个问题可以在这里的OpenJdk bug跟踪器中找到


目前,在Jdk 9发布之前,这个问题还没有得到解决。我已经把它作为一个bug提交了:这个bug在最新的Jdk 7(9版)中没有得到解决。bug报告不再可见,苹果最新的JDK 6推送将Java 7作为默认版本,并删除Java首选项应用程序。有趣的时刻。非常有趣的是,你看到它被原始值和双键事件调用。我只是重新运行了我的应用程序(不是示例),并确认了最初的行为<使用空字符串调用code>setValueAt()
,我没有观察到双键事件。运行您的示例,我得到了替换为相同的字符串行为,但不是双键事件。我把表格放在一个滚动窗格中,并在表格上调用setCellSelectionEnabled(true),以使其更符合应用程序。我使用
build 1.7.0_04-ea-b11
(默认情况下获得的JDK)@Bill我添加了一个小编辑。看起来
setValueAt
调用与我的机器上按下的两个
键直接相关我现在无法检查,但昨晚我想我注意到(在启用单元格选择的情况下)对
setValueAt
的伪调用仅在上/下移动而非左/右移动时发生。编辑顺便说一句,我现在在最新的下载。u09 IIRC。谢谢,但那真的不行。
putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE)
 if(Platform.isOSX())
        {
            if(value.equals(""))
            {
                return;
            }
        }