InputVerifier错误地产生焦点,需要使用它与其他ActionListener方法的建议

时间:2013-02-07 16:37:00

标签: java actionlistener inputverifier

我有一个GUI,用户可以在其中输入多个字段的测量结果,并根据测量值计算结果。我正在尝试为字段实现以下内容 -

  1. 输入的值必须在适当的范围内
  2. 我有一个选项对话框,除其他外,设置单位。任何具有值的字段必须更新为当前单位
  3. 当字段中的值发生变化时,我需要查看是否输入了所有测量值,如果是,则执行(或重做)计算。
  4. 我用表做了类似的事情(模型保留了'标准'单位的值,自定义渲染器和单元格编辑器处理显示用户当前单位的值并将值存储在'标准'单位在模型中)。

    我不相信JTextField有一个渲染的渲染器,文档编辑器看起来有点令人生畏,而且用户不喜欢JFormattedTextField,所以我想我会创建一个存储'标准'值的自定义JTextField使用我之前用于表的inputVerifier。

    示例代码如下(它几乎可以工作)。我使用JComboBox作为选项对话框的替身,并且只实现了一个文本字段。

    我可以使用一些专家建议 -

    1. 我可能会误解setInputVerifier。我认为如果我试图从文本字段更改焦点并且如果我说不产生焦点则将焦点保持在文本字段中,则应该调用它。但是如果我在文本字段中放入'aa'(没有按Enter键),我可以更改组合框中的值。我的调试println说 -
    2. 音量值改变了(f)//我的焦点听众被解雇了 从我的焦点监听器//更新模型 验证:'aa'//来自我的输入验证程序 来自我的输入验证程序的数字//无效

      文本框呈红色轮廓,我听到一声嘟嘟声,但组合框处于活动状态。文本字段以空值结束,因为当我更改其值时会调用组合框动作侦听器。为什么我可以更改combox值?我该怎么做呢?

      1. 我添加了一个InputVerifier,两个ActionListener和一个FocusListener似乎错了。我确实喜欢任务的逻辑分离。我该怎么办?我应该扩展DoubleVerifier并覆盖actionPerformed以包含DoubleVerifier中当前的内容以及VolumeValueListener中的内容吗?
      2. 我希望验证文本字段,并在用户输入(CR)并保留在字段中或离开字段时更新基础数据视图。这就是为什么动作和焦点听众。

        欢迎任何更正或见解。

        UnitsTextField.java

        import java.util.*;
        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;
        
        class UnitsTextField extends JTextField
        {
           Double modelValue = null;
           Double viewValue  = null;
        
           UnitsTextField( int cols )
           {
              super( cols );
           }
        
           public void updateModel() throws Exception
           {
              System.out.println( "Updating model" );
              modelValue = Conversion.modelValue( this.getText() );
           }
        
           public void refreshView()
           {
              this.setText( Conversion.viewString( modelValue ) );
           }
        
           public Double getModelValue()
           {
              return modelValue;
           }
        } 
        

        UnitsLabel.java

        import java.util.*;
        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;
        
        class UnitsLabel extends JLabel
        {
           public void refreshView()
           {
              super.setText( Conversion.viewLabel() );
           }
        }
        

        Conversion.java

        import java.util.*;
        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;
        
        class Conversion
        {
           public  static enum  UNITS {CC, L, GAL};
        
           public  static Map<String,UNITS> unitTypes = 
                                               new HashMap<String, UNITS>()
           {
              {
                 put( "Cubic centimeters", UNITS.CC  );
                 put( "Liters",            UNITS.L   );
                 put( "Gallons",           UNITS.GAL );
              }
           };
        
           public  static Map<UNITS,Double> unitConversions =
                                               new HashMap<UNITS, Double>()
           {
              {
                 put( UNITS.CC,     1.0 );
                 put( UNITS.L,   1000.0 );
                 put( UNITS.GAL, 4404.9 );
              }
           };
        
           private static UNITS unitType = UNITS.CC;
        
           public static void   setUnitType( UNITS unit )
           {
              unitType = unit;
           }
        
           public static void   setUnitType( String unitString )
           {
              unitType = unitTypes.get(unitString);
           }
        
           public static String[] getUnitNames()
           {
              return (unitTypes.keySet()).toArray(new String[0]);
           }
        
           public static String viewLabel()
           {
              return unitType.toString();
           }
        
           public static Double modelValue( String viewString ) throws Exception
           {
              Double value = null;
        
              if (viewString != null && viewString.length() > 0)
              {
                 value = Double.parseDouble( viewString );
                 value = value * unitConversions.get(unitType);
              }
              return value;
           }
        
           public static String viewString( Double modelValue )
           {
              Double value = null;
        
              if (modelValue != null)
              {
                 value = modelValue / unitConversions.get(unitType);
              }
              return (value == null) ? "" : value.toString();
           }
        }
        

        DoubleVerifier.java

        import java.util.*;
        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;
        import javax.swing.border.*;
        import java.text.NumberFormat;
        import java.awt.Toolkit;
        
        public class DoubleVerifier extends InputVerifier implements ActionListener
        {
           public boolean shouldYieldFocus(JComponent input)
           {
              JTextField tf   = (JTextField) input;
              boolean inputOK = verify(input);
        
              if (inputOK)
              {
                 tf.setBorder( new LineBorder( Color.black ) );
                 return true;
              }
              else
              {
                 tf.setBorder( new LineBorder( Color.red ) );
                 Toolkit.getDefaultToolkit().beep();
                 return false;
              }
           }
        
           public boolean verify(JComponent input)
           {
              JTextField tf  = (JTextField) input;
              String     txt = tf.getText();
              double     n;
        
              System.out.println( "Verifying: '" + txt + "'" );
              if (txt.length() != 0)
              {
                 try
                 {
                    n = Double.parseDouble(txt);
                 }
                 catch (NumberFormatException nf)
                 {
                    System.out.println( "Invalid number" );
                    return false;
                 }
              }
              return true;
           }
        
           public void actionPerformed(ActionEvent e)
           {
              System.out.println( "Input verification" );
              JTextField source = (JTextField) e.getSource();
              shouldYieldFocus(source);
           }
        }
        

        VolumeTextFieldTest.java

        import java.util.*;
        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;
        import javax.swing.border.*;
        
        class VolumeTextFieldTest extends JFrame
        {
           private JComboBox      volumeCombo;
           private UnitsLabel     volumeLabel;
           private UnitsTextField volumeField;
        
           public VolumeTextFieldTest()
           {
              setSize(300, 100);
              setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              volumeCombo   = new JComboBox( Conversion.getUnitNames() );
              volumeCombo.addActionListener( new VolumeListener() );
              volumeCombo.addFocusListener( new VolumeListener() );
              volumeLabel   = new UnitsLabel();
              volumeLabel.refreshView();
              volumeField   = new UnitsTextField(8);
              DoubleVerifier dVerify = new DoubleVerifier();
              volumeField.setInputVerifier(  dVerify );
              volumeField.addActionListener( dVerify );
              volumeField.addActionListener( new VolumeValueListener() );
              volumeField.addFocusListener(  new VolumeValueListener() );
              JPanel myPane = new JPanel();
              myPane.add(volumeCombo);
              myPane.add(volumeField);
              myPane.add(volumeLabel);
              getContentPane().add(myPane);
              setVisible(true);
           }
        
           public class VolumeListener implements ActionListener, FocusListener
           {
              @Override
              public void actionPerformed( ActionEvent ae )
              {
                  System.out.println( "Volume type changed" );
             Conversion.setUnitType( (String) volumeCombo.getSelectedItem() );
             volumeLabel.refreshView();
                  volumeField.refreshView();
              }
              @Override
              public void focusGained( FocusEvent fg )
              {
              }
              @Override
              public void focusLost( FocusEvent fl )
              {
                  System.out.println( "Volume type changed" );
             Conversion.setUnitType( (String) volumeCombo.getSelectedItem() );
             volumeLabel.refreshView();
                  volumeField.refreshView();
              }
           }
        
           public class VolumeValueListener implements ActionListener, FocusListener
           {
              @Override
              public void actionPerformed( ActionEvent ae )
              {
                 System.out.println( "Volume value changed (a)" );
                 try
                 {
                volumeField.updateModel();
                volumeField.refreshView();
                 }
                 catch (Exception e)
                 {}
              }
              @Override
              public void focusGained( FocusEvent fg )
              {
              }
              @Override
              public void focusLost( FocusEvent fl )
              {
                 System.out.println( "Volume value changed (f)" );
                 try
                 {
                volumeField.updateModel();
                volumeField.refreshView();
                 }
                 catch (Exception e)
                 {}
              }
           }
        
           public static void main(String[] args)
           {
              try
              {
                 SwingUtilities.invokeLater( new Runnable()
                 {
                    public void run ()
                    {
                       VolumeTextFieldTest runme = new VolumeTextFieldTest();
                    }
                 });
              }
              catch (Exception e)
              {
                 System.out.println( "GUI did not start" );
              }
           }
        }
        

1 个答案:

答案 0 :(得分:0)

我从其他研究中了解了部分问题。 InputVerifier只关注焦点。如果输入无效,则它不会传输焦点,但是,它将允许动作事件发生。我看到的投诉与那些有退出按钮的人有关,即使某个字段中的数据无效,也会执行该操作。在我的情况下,我有一个组合框,即使InputVerifier抱怨无效数据(文本字段有红色边框和哔哔声),仍然可以执行其操作。所以关于问题的这个方面,我不相信有一个好的解决方案。一个建议是所有动作侦听器在执行动作之前检查的变量,该变量将由InputVerifier设置。我已将(理想情况下)可重用的例程放在单独的文件中,因此这个解决方案会遇到一些问题。

我仍然不确定如何优雅地处理我有几个不同的通用操作(验证输入,转换单位,更新视图)的情况,其中只有一些将需要任何给定的字段,我想分配ActionListeners和FocusListeners按顺序排列。我现在唯一的想法是有一个基本监听器,例如验证输入,然后扩展它并覆盖actionPerformed,focusGained和focusLost方法,虽然看起来我最终会为每个组合复制代码。