简体   繁体   English

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

[英]InputVerifier incorrectly yielding focus and need advice using it with other ActionListener methods

I have a GUI in which the user enters measurements into a number of fields and calculates a result based on the measurement. 我有一个GUI,用户可以在其中将测量值输入许多字段,并根据测量值计算结果。 I am trying to implement the following for the fields- 我正在尝试对以下字段实施以下操作:

  1. The values entered must be in the proper range 输入的值必须在正确的范围内
  2. I have an option dialog that among other things, sets the units. 我有一个选项对话框,其中包括设置单位。 Any field that has a value in it, must be updated to the current units 任何包含值的字段都必须更新为当前单位
  3. When a value in a field changes, I need to see if all the measurements are entered, and if so, do (or redo) the calculation. 当字段中的值更改时,我需要查看是否输入了所有度量,如果是,则进行(或重做)计算。

I've done something like this with tables (the model retained the values in 'standard' units and a custom renderer and cell editor handled showing the user the values in the current units and storing the values in the 'standard' units in the model). 我已经使用表格做了类似的事情(模型以“标准”单位保留值,并且自定义渲染器和单元格编辑器处理了向用户显示当前单位的值并将值存储在模型中的“标准”单位中的情况) )。

I don't believe JTextField has a renderer to override, the Document editor looked a bit daunting, and the user didn't like the JFormattedTextField, so I thought I would create a custom JTextField that stored a 'standard' value and use the inputVerifier that I used previously for the table. 我不相信JTextField可以覆盖渲染器,Document编辑器看起来有些令人生畏,并且用户不喜欢JFormattedTextField,所以我想我会创建一个自定义JTextField来存储“标准”值并使用inputVerifier我以前在桌子上使用过的

Example code is below (it almost works). 示例代码如下(几乎可以使用)。 I used a JComboBox as a stand-in for the option dialog and implemented only one text field. 我使用JComboBox作为选项对话框的替代,仅实现了一个文本字段。

I could use some expert advice- 我可以使用一些专家意见-

  1. I may be misunderstanding setInputVerifier. 我可能会误解setInputVerifier。 I thought it should be invoked if I attempted to change focus from the text field and keep focus in the text field if I said don't yield focus. 我认为如果我尝试从文本字段更改焦点并在我说不产生焦点的情况下保持焦点在文本字段中,则应该调用它。 But if I put 'aa' in the text field (without hitting enter) I can change the value in the combobox. 但是,如果我在文本字段中输入“ aa”(不按Enter),则可以更改组合框中的值。 My debugging println say- 我的调试println说-

Volume value changed (f) // my focus listener fired off Updating model // from my focus listener Verifying: 'aa' // from my input verifier Invalid number // from my input verifier 体积值已更改(f)//我的焦点侦听器已启动//从我的焦点侦听器更新模型//验证:'aa'//来自我的输入验证器//无效的数字//来自我的输入验证器

the text box gets a red outline and I hear a beep, but the combobox is active. 文本框显示为红色轮廓,并且听到哔声,但组合框处于活动状态。 The text field ends up with an empty value, since the combobox action listener is called when I change its value. 文本字段以空值结尾,因为当我更改其值时会调用组合框动作监听器。 Why am I allowed to change the combox value? 为什么允许我更改combox的值? How do I stop that? 我该如何阻止呢?

  1. My adding an InputVerifier, two ActionListeners and a FocusListener seems wrong. 我添加了InputVerifier,两个ActionListener和FocusListener似乎是错误的。 I do like the logical separation of tasks. 我喜欢逻辑上的任务分离。 What should I be doing? 我该怎么办? Should I extend DoubleVerifier and override the actionPerformed to just include what's currently in DoubleVerifier and what is in the VolumeValueListener? 我是否应该扩展DoubleVerifier并覆盖actionPerformed以仅包括DoubleVerifier中当前的内容和VolumeValueListener中的内容?

I want the text field to be validated and the view of the underlying data updated either when the user enters (CR) and stays in the field or when they leaves the field. 我希望验证文本字段,并在用户输入(CR)并停留在字段中或离开字段时更新基础数据的视图。 Which is why the action and focus listeners. 这就是为什么动作和焦点侦听器如此。

Any corrections or insights are most welcome. 任何改正或见解是最欢迎的。

UnitsTextField.java 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 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 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 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 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" );
      }
   }
}

I understand part of my problem from additional research. 通过其他研究,我了解了部分问题。 InputVerifier is only concerned with focus. InputVerifier仅关注焦点。 If the input is invalid, then it will not transfer focus, however, it will allow action events to occur. 如果输入无效,那么它将不会转移焦点,但是,它将允许发生操作事件。 The complaints I have seen have been related to people who had an exit button whose action would be performed even if the data in a field was invalid. 我所看到的投诉与那些具有退出按钮的人有关,即使该字段中的数据无效,该按钮也将执行操作。 In my case, I have a combobox whose action can still be performed even though the InputVerifier is complaining about the invalid data (text field gets a red border and beeps). 就我而言,我有一个组合框,即使InputVerifier抱怨无效数据(文本字段显示红色边框并发出哔哔声),其操作仍然可以执行。 So in respect to that aspect of the problem, I don't believe there is a good solution. 因此,就问题的这一方面而言,我认为没有好的解决方案。 One suggestion was a variable that all the action listeners checked before performing the action, which would be set by the InputVerifier. 一个建议是一个变量,在执行操作之前,所有操作侦听器都会对其进行检查,该变量将由InputVerifier设置。 I've got my (ideally) reusable routines in separate files, so will have some problems with this solution. 我的(理想情况下)可重用的例程位于单独的文件中,因此此解决方案将存在一些问题。

I'm still unsure of how to gracefully handle the situation where I have a several distinct generic actions (verify input, convert units, update a view) where only some will be needed for any given field and I want to assign ActionListeners and FocusListeners in sequential order. 我仍然不确定如何妥善处理以下情况:我有几个不同的通用动作(验证输入,转换单位,更新视图),其中任何给定字段仅需要一些动作,并且我想在其中分配ActionListener和FocusListener顺序顺序。 My only thought right now is to have a base listener, that for example verifies input, then extend it and override the actionPerformed, focusGained and focusLost methods, though it seems like I will end up duplicating code for every combination. 我现在唯一的想法就是拥有一个基本的侦听器,例如,验证输入,然后扩展输入并覆盖actionPerformed,focusGained和focusLost方法,尽管看起来我最终将为每个组合复制代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM