Java InputVerifier错误地产生焦点,需要建议将其与其他ActionListener方法一起使用

Java InputVerifier错误地产生焦点,需要建议将其与其他ActionListener方法一起使用,java,actionlistener,inputverifier,Java,Actionlistener,Inputverifier,我有一个GUI,用户在其中将测量值输入多个字段,并根据测量值计算结果。我正在尝试为字段实现以下内容- 输入的值必须在正确的范围内 我有一个选项对话框,其中包括设置单位。任何包含值的字段都必须更新为当前单位 当字段中的值发生变化时,我需要查看是否输入了所有测量值,如果输入了,则执行(或重做)计算 我对表格做过类似的操作(模型保留了“标准”单位中的值,并处理了一个自定义渲染器和单元编辑器,向用户显示当前单位中的值,并将值存储在模型中的“标准”单位中) 我不相信JTextField有一个要重写的渲染器

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

  • 输入的值必须在正确的范围内
  • 我有一个选项对话框,其中包括设置单位。任何包含值的字段都必须更新为当前单位
  • 当字段中的值发生变化时,我需要查看是否输入了所有测量值,如果输入了,则执行(或重做)计算
  • 我对表格做过类似的操作(模型保留了“标准”单位中的值,并处理了一个自定义渲染器和单元编辑器,向用户显示当前单位中的值,并将值存储在模型中的“标准”单位中)

    我不相信JTextField有一个要重写的渲染器,文档编辑器看起来有点令人生畏,而且用户不喜欢JFormattedTextField,所以我想我应该创建一个自定义的JTextField来存储一个“标准”值,并使用我以前在表中使用的inputVerifier

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

    我需要一些专家的建议-

  • 我可能误解了setInputVerifier。我认为如果我试图从文本字段更改焦点,并且如果我说不产生焦点,则应该在文本字段中保持焦点,那么应该调用它。但是,如果我在文本字段中输入'aa'(不按enter键),我可以更改组合框中的值。我的朋友说-
  • 音量值已更改(f)//我的焦点侦听器已启动 从我的焦点侦听器更新模型// 正在验证:“aa”//来自我的输入验证程序 无效的数字//来自我的输入验证器

    文本框有一个红色的轮廓,我听到一声蜂鸣,但是组合框是活动的。文本字段以空值结束,因为当我更改其值时会调用combobox操作侦听器。为什么允许我更改combox值?我怎样才能阻止它

  • 我添加一个InputVerifier、两个ActionListener和一个FocusListener似乎是错误的。我确实喜欢任务的逻辑分离。我该怎么办?我是否应该扩展DoubleVerifier并覆盖执行的操作,以便只包括DoubleVerifier中当前的内容和VolumeValueListener中的内容
  • 我希望在用户输入(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();
       }
    }
    
    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" );
          }
       }
    }
    

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


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

    geez太多了抱歉,实现函数的最低要求是在文本字段中存储模型值,然后让它显示不同的视图并进行验证。您想要一个只显示文本字段对组合框失去焦点的示例吗?那会短一点,最好是这样。如果您知道最有可能导致问题的代码段(在本例中,似乎是纯粹的Swing代码,尽管情况可能并非如此),那么最好提供SSCCE—请参阅以了解更多详细信息。这样一来,问题就与重要的问题隔离开来,你会因为得到帮助而得到更好的改变:)
    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" );
          }
       }
    }