在使用该表格示例时,似乎存在不止一个错误。
我使用了以下 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;
}
使用向上和向下箭头导航使选择一次跳转 2 行。Stacktraces 揭示了选择的变化源于BasicTableUI#Actions
类(这是有道理的,因为这是放置在动作映射中的动作)。奇怪的是,对于一个按键,这个动作会触发两次。这已经解释了为什么选择一次会跳转 2 行。进一步的调试显示,一旦我收到两个不同的KEY_PRESSED
事件,就按下箭头键。据我所知,这些事件是这样放置的,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 );
}
}
将焦点放在框架上,然后点击 DOWN_ARROW 会产生以下输出(剥离输出toString
以保持可读性)
keyevent.getKeyCode() = 40
ID = 960135925
keyevent = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=40,...
keyevent.getKeyCode() = 40
ID = 1192754471
keyevent = java.awt.event.KeyEvent[KEY_PRESSED,keyCode=40,...
keyevent.getKeyCode() = 40
ID = 2012032999
keyevent = java.awt.event.KeyEvent[KEY_RELEASED,keyCode=40,...
在这里你可以清楚地看到你得到了两个KEY_PRESSED
混淆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:
KEY_TYPED(仅在可以生成有效的 Unicode 字符时生成。)
有道理我KEY_TYPED
在击中箭头时不会收到该事件,但它会触发KEY_PRESSED
两次在我看来是一个错误(稍后将为此记录一个错误报告)。一种解决方法可能是拦截这样的事件而不是通过链传递它,但这对我来说听起来像是一个丑陋的黑客攻击。
编辑
另一个奇怪的事情。如果您将以下行添加到代码段
table.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).
put( KeyStroke.getKeyStroke( 'a' ), "selectNextRow" );
您可以使用a
跳转到下一行(与默认情况下使用 DOWN_ARROW 触发的操作相同)。由于按下时事件的正确顺序a
,似乎该setValueAt
方法也没有被调用。这让我觉得这两个KEY_PRESSED
事件以某种方式开始了编辑......