を使用してテキスト フィールドのユーザー入力を検証しようとしていますがjavax.swing.InputVerifier
、入力の検証は期待どおりに機能しますが、プロパティに関して問題がVerifyInputWhenFocusTarget
あります。
サブクラスのステータスとオーバーライドverify()
およびshouldYieldFocus()
メソッドを表示するラベルを作成しましたが、それは正常に機能します。InputVerifier
私がやりたかった次のステップは、ボタンの を設定して、現在のフォーカス所有者の検証が false の場合にフォーカスを取得しないようにすることでしたが、プロパティをVerifyInputWhenFocusTarget
に設定した効果に気付かず、ボタンが現在のフォーカス所有者の検証が false の場合でも押されます。VerifyInputWhenFocusTarget
true
ドキュメントを理解していない可能性があります-ボタンのVerifyInputWhenFocusTarget
プロパティをに設定するtrue
と、テキストフィールドの誤った検証の状況下でクリックしたときにボタンがフォーカスを取得できないと思いました。さらに、ボタンがフォーカスを取得できない場合、そのactionPerformed()
メソッドが呼び出されないことを (誤解して) 理解していました。
actionPerformed()
ただし、によって「保護」されたテキスト フィールドの誤った検証にもかかわらず、ボタンをクリックすることができ、そのメソッドが実行されjavax.swing.InputVerifier
ます。
削除されたコードは次のとおりです。
package verifiertest;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.UIManager;
import java.awt.GridLayout;
import java.math.BigDecimal;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingConstants;
import javax.swing.JTextField;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class TestVerifier {
private JFrame frmInputverifierTest;
private JTextField tfFirstNum;
private JTextField tfSecondNum;
private JLabel lblStatus;
private String statusText = "Input the numbers and press the \"Start!\" button...";
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestVerifier window = new TestVerifier();
window.frmInputverifierTest.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TestVerifier() {
initialize();
}
private void initialize() {
frmInputverifierTest = new JFrame();
frmInputverifierTest.setTitle("InputVerifier Test");
frmInputverifierTest.setBounds(100, 100, 500, 450);
frmInputverifierTest.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panelContainer = new JPanel();
panelContainer.setBorder(new EmptyBorder(5, 5, 5, 5));
frmInputverifierTest.getContentPane().add(panelContainer, BorderLayout.CENTER);
panelContainer.setLayout(new BorderLayout(0, 0));
JPanel panelInput = new JPanel();
panelInput.setBorder(new TitledBorder(null, "Input", TitledBorder.LEADING, TitledBorder.TOP, null, null));
panelContainer.add(panelInput, BorderLayout.NORTH);
panelInput.setLayout(new GridLayout(2, 2, 10, 4));
JLabel lblFirstNum = new JLabel("Number #1:");
lblFirstNum.setHorizontalAlignment(SwingConstants.TRAILING);
panelInput.add(lblFirstNum);
tfFirstNum = new JTextField();
panelInput.add(tfFirstNum);
tfFirstNum.setColumns(10);
// setup the verifier
MyTxtVerifier txtVerifier = new MyTxtVerifier();
tfFirstNum.setInputVerifier(txtVerifier);
JLabel lblSecondNum = new JLabel("Number #2:");
lblSecondNum.setHorizontalAlignment(SwingConstants.TRAILING);
panelInput.add(lblSecondNum);
tfSecondNum = new JTextField();
panelInput.add(tfSecondNum);
tfSecondNum.setColumns(10);
// setup the verifier
tfSecondNum.setInputVerifier(txtVerifier);
JPanel panelOutput = new JPanel();
panelOutput.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Output (not used at the moment)", TitledBorder.LEADING, TitledBorder.TOP, null, null));
panelContainer.add(panelOutput, BorderLayout.CENTER);
JPanel panelSouth = new JPanel();
panelSouth.setBorder(null);
panelContainer.add(panelSouth, BorderLayout.SOUTH);
panelSouth.setLayout(new GridLayout(0, 1, 0, 0));
JPanel panelStatus = new JPanel();
FlowLayout flowLayout_1 = (FlowLayout) panelStatus.getLayout();
flowLayout_1.setAlignment(FlowLayout.LEFT);
panelStatus.setBorder(new TitledBorder(null, "Status", TitledBorder.LEADING, TitledBorder.TOP, null, null));
panelSouth.add(panelStatus);
lblStatus = new JLabel(statusText);
panelStatus.add(lblStatus);
JPanel panelActions = new JPanel();
panelActions.setBorder(new TitledBorder(null, "Actions", TitledBorder.LEADING, TitledBorder.TOP, null, null));
FlowLayout flowLayout = (FlowLayout) panelActions.getLayout();
flowLayout.setAlignment(FlowLayout.RIGHT);
panelSouth.add(panelActions);
JButton btnHelp = new JButton("?");
btnHelp.setVerifyInputWhenFocusTarget(false); // <-- NO EFFECT!?
btnHelp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frmInputverifierTest, "Help button pressed...", "Help", JOptionPane.PLAIN_MESSAGE);
}
});
panelActions.add(btnHelp);
JButton btnStart = new JButton("Start!");
btnStart.setVerifyInputWhenFocusTarget(true); // <-- NO EFFECT!?
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frmInputverifierTest, "Start button pressed...", "Start", JOptionPane.PLAIN_MESSAGE);
}
});
panelActions.add(btnStart);
btnHelp.setPreferredSize(btnStart.getPreferredSize()); // make buttons equal in size
}
// an inner class so it can access parent fields
public class MyTxtVerifier extends InputVerifier {
// This method should have no side effects
@Override
public boolean verify(JComponent input) {
String text = ((JTextField)input).getText();
try {
BigDecimal value = new BigDecimal(text);
if(value.floatValue() <= 0.0)
return false;
return (value.scale() <= Math.abs(4)); // why not 4 instead of Math.abs(4)??
} catch (Exception e) {
return false;
}
}
// This method can have side effects
@Override
public boolean shouldYieldFocus(JComponent input) {
String statusOld, status;
statusOld = statusText; // remember the original text
boolean isOK = verify(input); // call overridden method
if(isOK)
status = statusOld;
else
status = "Error: The parameter should be a positive number up to 4 decimal places";
lblStatus.setText(status);
// return super.shouldYieldFocus(input);
return isOK;
}
}
}
テスト アプリケーションのスクリーンショットは次のとおりです。
ご覧のとおり、ボタンが 2 つあります。それらの 1 つはVerifyInputWhenFocusTarget
プロパティが に設定されtrue
、もう 1 つは同じプロパティが に設定されてfalse
いますが、テキスト フィールドの検証が誤っている状況でボタンをクリックしても違いはありません。誤った検証は、負の数または 4 桁を超える数を入力することによって引き起こされる可能性があります。InputVerifier
確かに、フォーカスを他のテキスト フィールドに移動することはできませんが、ボタンをアクティブにすることはできません。最初にフォーカスを取得せずにボタンをアクティブにすることは (少なくとも私には) 意味がないためStart!、テキスト フィールドの検証メソッドverify()
が返されたときにボタンをアクティブにする可能性はありませんfalse
。
編集:
Trashgod の提案に対応するようにコードを変更しました (Start!ボタンの有効状態を で条件付けますFocusListener
)。実際の例は次のとおりです。
package verifiertest;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.text.JTextComponent;
import javax.swing.UIManager;
import java.awt.GridLayout;
import java.math.BigDecimal;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingConstants;
import javax.swing.JTextField;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ActionEvent;
public class TestVerifier implements FocusListener {
private JFrame frmInputverifierTest;
private JTextField tfFirstNum;
private JTextField tfSecondNum;
private JLabel lblStatus;
private JButton btnStart;
private String statusText = "Input the numbers and press the \"Start!\" button...";
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestVerifier window = new TestVerifier();
window.frmInputverifierTest.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TestVerifier() {
initialize();
}
private void initialize() {
frmInputverifierTest = new JFrame();
frmInputverifierTest.setTitle("InputVerifier Test");
frmInputverifierTest.setBounds(100, 100, 500, 450);
frmInputverifierTest.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panelContainer = new JPanel();
panelContainer.setBorder(new EmptyBorder(5, 5, 5, 5));
frmInputverifierTest.getContentPane().add(panelContainer, BorderLayout.CENTER);
panelContainer.setLayout(new BorderLayout(0, 0));
JPanel panelInput = new JPanel();
panelInput.setBorder(new TitledBorder(null, "Input", TitledBorder.LEADING, TitledBorder.TOP, null, null));
panelContainer.add(panelInput, BorderLayout.NORTH);
panelInput.setLayout(new GridLayout(2, 2, 10, 4));
JLabel lblFirstNum = new JLabel("Number #1:");
lblFirstNum.setHorizontalAlignment(SwingConstants.TRAILING);
panelInput.add(lblFirstNum);
tfFirstNum = new JTextField();
panelInput.add(tfFirstNum);
tfFirstNum.setColumns(10);
// setup the verifier
MyTxtVerifier txtVerifier = new MyTxtVerifier();
tfFirstNum.setInputVerifier(txtVerifier);
// add focus listener
tfFirstNum.addFocusListener(this);
JLabel lblSecondNum = new JLabel("Number #2:");
lblSecondNum.setHorizontalAlignment(SwingConstants.TRAILING);
panelInput.add(lblSecondNum);
tfSecondNum = new JTextField();
panelInput.add(tfSecondNum);
tfSecondNum.setColumns(10);
// setup the verifier
tfSecondNum.setInputVerifier(txtVerifier);
// add focus listener
tfSecondNum.addFocusListener(this);
JPanel panelOutput = new JPanel();
panelOutput.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Output (not used at the moment)", TitledBorder.LEADING, TitledBorder.TOP, null, null));
panelContainer.add(panelOutput, BorderLayout.CENTER);
JPanel panelSouth = new JPanel();
panelSouth.setBorder(null);
panelContainer.add(panelSouth, BorderLayout.SOUTH);
panelSouth.setLayout(new GridLayout(0, 1, 0, 0));
JPanel panelStatus = new JPanel();
FlowLayout flowLayout_1 = (FlowLayout) panelStatus.getLayout();
flowLayout_1.setAlignment(FlowLayout.LEFT);
panelStatus.setBorder(new TitledBorder(null, "Status", TitledBorder.LEADING, TitledBorder.TOP, null, null));
panelSouth.add(panelStatus);
lblStatus = new JLabel(statusText);
panelStatus.add(lblStatus);
JPanel panelActions = new JPanel();
panelActions.setBorder(new TitledBorder(null, "Actions", TitledBorder.LEADING, TitledBorder.TOP, null, null));
FlowLayout flowLayout = (FlowLayout) panelActions.getLayout();
flowLayout.setAlignment(FlowLayout.RIGHT);
panelSouth.add(panelActions);
JButton btnHelp = new JButton("?");
btnHelp.setVerifyInputWhenFocusTarget(false); // <-- NO EFFECT!?
btnHelp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frmInputverifierTest, "Help button pressed...", "Help", JOptionPane.PLAIN_MESSAGE);
}
});
panelActions.add(btnHelp);
btnStart = new JButton("Start!");
btnStart.setEnabled(false);
btnStart.setVerifyInputWhenFocusTarget(true); // <-- NO EFFECT!?
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frmInputverifierTest, "Start button pressed...", "Start", JOptionPane.PLAIN_MESSAGE);
}
});
panelActions.add(btnStart);
btnHelp.setPreferredSize(btnStart.getPreferredSize()); // make buttons equal in size
}
// an inner class so it can access parent fields
public class MyTxtVerifier extends InputVerifier {
// This method should have no side effects
@Override
public boolean verify(JComponent input) {
String text = ((JTextField)input).getText();
// to allow changing focus when nothing is entered
if(text.isEmpty())
return true;
try {
BigDecimal value = new BigDecimal(text);
if(value.floatValue() <= 0.0)
return false;
return (value.scale() <= Math.abs(4)); // why not 4 instead of Math.abs(4)??
} catch (Exception e) {
return false;
}
}
// This method can have side effects
@Override
public boolean shouldYieldFocus(JComponent input) {
String statusOld, status;
statusOld = statusText; // remember the original text
boolean isOK = verify(input); // call overridden method
if(isOK)
status = statusOld;
else {
btnStart.setEnabled(false);
status = "Error: The parameter should be a positive number up to 4 decimal places";
}
lblStatus.setText(status);
// return super.shouldYieldFocus(input);
return isOK;
}
}
@Override
public void focusGained(FocusEvent e) {
// nothing to do on focus gained
}
@Override
public void focusLost(FocusEvent e) {
// in case we want to show a message box inside focusLost() - not to be fired twice
if(e.isTemporary())
return;
final JTextComponent c = (JTextComponent)e.getSource();
// in case there are more text fields but
// we are validating only some of them
if(c.equals(tfFirstNum) || c.equals(tfSecondNum)) {
// are all text fields valid?
if(c.getInputVerifier().verify(tfFirstNum) && c.getInputVerifier().verify(tfSecondNum) &&
!tfFirstNum.getText().isEmpty() && !tfSecondNum.getText().isEmpty())
btnStart.setEnabled(true);
else
btnStart.setEnabled(false);
}
}
}
メソッドのコードを少し変更して、verify()
何も入力されていない場合にフォーカスを変更できるようにしました (focusLost()
メソッドは、すべてのテキスト フィールドに何らかの入力が含まれているかどうかをチェックしverify()
、各テキスト フィールドを明示的に呼び出して入力が有効かどうかもチェックします)。
もちろん、コードには若干の微調整 (タブ オーダー、ステータスの更新など) が必要ですが、それはこのトピックの範囲外です。
結論:
VerifyInputWhenFocusTarget
プロパティは明らかに便利ですが (この例では、?テキスト フィールドの検証が の場合でもボタンにフォーカスを当てることができますfalse
)、私はまだ、ドキュメンテーションがすべての重要な副作用を説明する上で正確ではないという私の意見を保持しています。直観に反するため、ドキュメントを読むだけでなく、さらにテストと調査 (おそらくソース コードの分析) を行う必要があります。