首页 > 解决方案 > JTextArea-Dialog 作为 JTable-CellEditor 错过了第一个键入的字符

问题描述

我们需要一个CellEditorfor aJTable来编辑一个大的多行文本。我们尝试使用一个在视觉上扩展 的弹出窗口TableCell,它与右侧和底部的单元格重叠。如果单元格位于右下角、靠近屏幕边界等,这会导致各种问题。

然后我们决定使用模式JDialog来编辑单元格值。所以用户可以移动对话框,我们可以保持它的大小和位置。

现在问题开始了;-)

我们无法将第一个键入的字符“转发”到对话框。 有很多关于堆栈溢出的示例,对于CellEditor直接显示在 Table(Cell) 中的自定义 s,此问题已解决,例如:Losing first character in JTable panel based cell editor

以下 SSCCE(来自 camickrs 的回答:https ://stackoverflow.com/a/3591230/361227 )显示第二个 TableColumn 中的第一个击键大部分时间都会丢失。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

/**
 * Example taken from this answer: https://stackoverflow.com/a/3591230/361227
 * 
 * @author camickr
 */
public class TablePopupEditor extends DefaultCellEditor
{
  private PopupDialog popup;
  private String      currentText = "";
  private JButton     editorComponent1;

  public TablePopupEditor()
  {
    super( new JTextField() );

    setClickCountToStart( 2 );

    //  Use a JButton as the editor component
    editorComponent1 = new JButton();
    editorComponent1.setBackground( Color.white );
    editorComponent1.setBorderPainted( false );
    editorComponent1.setContentAreaFilled( false );

    //  Set up the dialog where we do the actual editing
    popup = new PopupDialog();
  }

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

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

    SwingUtilities.invokeLater( new Runnable()
    {
      @Override
      public void run()
      {
        System.out.println( "run" );
        popup.setText( currentText );
        Point p = editorComponent1.getLocationOnScreen();
        popup.setLocation( p.x, p.y + editorComponent1.getSize().height );
        popup.setVisible( true );
        fireEditingStopped();
      }
    } );

    currentText = value.toString();
    editorComponent1.setText( currentText );
    return editorComponent1;
  }

  /*
  *   Simple dialog containing the actual editing component
  */
  class PopupDialog extends JDialog implements ActionListener
  {
    private JTextArea textArea;

    public PopupDialog()
    {
      super( (Frame) null, "Change Description", true );

      textArea = new JTextArea( 5, 20 );
      textArea.setLineWrap( true );
      textArea.setWrapStyleWord( true );
      KeyStroke keyStroke = KeyStroke.getKeyStroke( "ENTER" );
      textArea.getInputMap().put( keyStroke, "none" );
      JScrollPane scrollPane = new JScrollPane( textArea );
      getContentPane().add( scrollPane );

      JButton cancel = new JButton( "Cancel" );
      cancel.addActionListener( this );
      JButton ok = new JButton( "Ok" );
      ok.setPreferredSize( cancel.getPreferredSize() );
      ok.addActionListener( this );

      JPanel buttons = new JPanel();
      buttons.add( ok );
      buttons.add( cancel );
      getContentPane().add( buttons, BorderLayout.SOUTH );
      pack();

      getRootPane().setDefaultButton( ok );
    }

    public void setText( String text )
    {
      textArea.setText( text );
    }

    /*
    *   Save the changed text before hiding the popup
    */
    @Override
    public void actionPerformed( ActionEvent e )
    {
      if ( "Ok".equals( e.getActionCommand() ) )
      {
        currentText = textArea.getText();
      }

      textArea.requestFocusInWindow();
      setVisible( false );
    }
  }

  public static void main( String[] args )
  {
    String[] columnNames = { "Item", "Description" };
    Object[][] data =
        { { "Item 1", "Description of Item 1" }, { "Item 2", "Description of Item 2" }, { "Item 3", "Description of Item 3" } };

    JTable table = new JTable( data, columnNames );
    table.getColumnModel().getColumn( 1 ).setPreferredWidth( 300 );
    table.setPreferredScrollableViewportSize( table.getPreferredSize() );
    JScrollPane scrollPane = new JScrollPane( table );

    // Use the popup editor on the second column
    TablePopupEditor popupEditor = new TablePopupEditor();
    table.getColumnModel().getColumn( 1 ).setCellEditor( popupEditor );

    JFrame frame = new JFrame( "Popup Editor Test" );
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.getContentPane().add( scrollPane );
    frame.pack();
    frame.setLocationRelativeTo( null );
    frame.setVisible( true );
  }
}

有没有可靠的方法来捕捉第一个字符?

标签: javaswingjtablejdialogtablecelleditor

解决方案


我什至无法弄清楚字符是如何附加到文本区域的。我尝试了大约 200 次调用编辑器,但它只出现了一次。所以显然存在一些时间问题。像这样的随机问题通常表明代码没有在 EDT 上执行。

无论如何,我想出了一个解决方法:

public Component getTableCellEditorComponent(
    JTable table, Object value, boolean isSelected, int row, int column)
{
    AWTEvent event = EventQueue.getCurrentEvent();

    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()
        {
            String append = "";

            if (event.getID() == KeyEvent.KEY_PRESSED)
            {
                KeyEvent ke = (KeyEvent)event;
                String keyText = ke.getKeyText(ke.getKeyCode());

                if (keyText.length() == 1)
                    append += ke.getKeyChar();
            }

            popup.setText(currentText + append);
            //popup.setLocationRelativeTo( editorComponent );
            Point p = editorComponent.getLocationOnScreen();
            popup.setLocation(p.x, p.y + editorComponent.getSize().height);
            popup.show();
            fireEditingStopped();
        }
    });

    currentText = value.toString();
    editorComponent.setText( currentText );
    return editorComponent;
}

上面的代码保存了用于调用编辑器的事件。因此,当显示弹出窗口时,它可以检查键事件并获取按下的字符。


推荐阅读