0

ユーザーがいくつかのフィールドに測定値を入力し、測定値に基づいて結果を計算するGUIがあります。私はフィールドに以下を実装しようとしています-

  1. 入力する値は適切な範囲内である必要があります
  2. とりわけ、単位を設定するオプションダイアログがあります。値が含まれるフィールドは、現在の単位に更新する必要があります
  3. フィールドの値が変更されたら、すべての測定値が入力されているかどうかを確認する必要があります。入力されている場合は、計算を実行(またはやり直し)します。

私はテーブルでこのようなことをしました(モデルは「標準」単位で値を保持し、カスタムレンダラーとセルエディターはユーザーに現在の単位の値を表示し、モデルの「標準」単位で値を保存します)。

JTextFieldにオーバーライドするレンダラーがあるとは思わない、ドキュメントエディターは少し気が遠くなるように見え、ユーザーはJFormattedTextFieldが気に入らなかったので、「標準」値を格納するカスタムJTextFieldを作成し、inputVerifierを使用すると思いました。以前にテーブルに使用したものです。

サンプルコードは以下のとおりです(ほとんど機能します)。オプションダイアログの代用としてJComboBoxを使用し、1つのテキストフィールドのみを実装しました。

私はいくつかの専門家のアドバイスを使うことができます-

  1. setInputVerifierを誤解している可能性があります。テキストフィールドからフォーカスを変更し、フォーカスを生成しないと言った場合はテキストフィールドにフォーカスを維持しようとすると、呼び出されるはずだと思いました。ただし、テキストフィールドに「aa」を入力すると(Enterキーを押さずに)、コンボボックスの値を変更できます。私のデバッグprintlnは言う-

ボリューム値が変更されました(f)//フォーカスリスナーが起動しましたモデルの更新//フォーカスリスナーから検証中:'aa'//入力ベリファイアから無効な番号//入力ベリファイアから

テキストボックスの輪郭が赤くなり、ビープ音が鳴りますが、コンボボックスはアクティブです。値を変更するとコンボボックスアクションリスナーが呼び出されるため、テキストフィールドは空の値になります。comboxの値を変更できるのはなぜですか?どうすればそれを止めることができますか?

  1. InputVerifier、2つのActionListeners、およびFocusListenerを追加するのは間違っているようです。私はタスクの論理的な分離が好きです。私は何をすべきですか?DoubleVerifierを拡張し、actionPerformedをオーバーライドして、現在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();
   }
}

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" );
      }
   }
}
4

1 に答える 1

0

I understand part of my problem from additional research. InputVerifier is only concerned with focus. 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). 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. 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. 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.

于 2013-02-09T20:48:03.790 に答える