我正在尝试使用javax.swing.InputVerifier
验证文本字段用户输入,并且输入验证按预期工作,但我遇到有关VerifyInputWhenFocusTarget
属性的问题。
我已制作了一个标签,以显示verify()
子类的状态和覆盖的shouldYieldFocus()
和InputVerifier
方法,并且工作正常。
我想要做的下一步是设置按钮的VerifyInputWhenFocusTarget
,以便在当前焦点所有者的验证为假的情况下不会获得焦点,但我没有注意到将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;
}
}
}
以下是测试应用程序的屏幕截图:
可以看出,有两个按钮。其中一个将VerifyInputWhenFocusTarget
属性设置为true
,另一个属性设置为false
,但在此情况下单击按钮时没有任何区别假文本字段验证。可以通过输入负数或超过4位十进制数的某个数字来激发错误验证。 InputVerifier
确实阻止将焦点转移到其他文本字段,但它不会阻止激活按钮。由于它没有意义(至少对我而言)按钮可以在没有首先获得焦点的情况下被激活,因此在文本字段时不应该激活 Start!按钮。验证方法verify()
已返回false
。
修改
我现在已更改代码以适应trashgod的建议(使用FocusListener
调节 Start!按钮的启用状态),这是工作示例:< / p>
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()
。
当然,代码需要一些小的调整(Tab键顺序,更新状态,......),但这超出了本主题的范围。
结论:
虽然VerifyInputWhenFocusTarget
属性显然很有用(在这个例子中,?按钮可以获得焦点,即使文本字段的验证是false
),我我仍然认为文档在描述所有重要的副作用时并不十分精确,这些副作用相当违反直觉,因此除了阅读文档之外,还需要进行进一步的测试和调查(可能是分析源代码)。
答案 0 :(得分:2)
您对setVerifyInputWhenFocusTarget()
的电话会产生影响 - 确切地说是确定&#34; 当前焦点所有者的输入验证者的效果将在此组件请求焦点之前调用。&#34; [强调我的]特别是,建立以下状态:
让statusText
具有初始值,或通过输入有效值来恢复初始值。
让tfFirstNum
或tfSecondNum
在包含无效值时具有焦点。
然后观察
点击?使statusText
保持不变,这意味着焦点组件的输入验证程序不被调用,如< / p>
btnHelp.setVerifyInputWhenFocusTarget(false);
点击开始设置statusText
以反映错误,这意味着已按照规定调用了焦点组件的输入验证程序 由
btnStart.setVerifyInputWhenFocusTarget(true);
请注意,必须满足上述两个条件,才能在点击任一按钮时看到效果。