我正在尝试使用验证文本字段用户输入javax.swing.InputVerifier
输入验证按预期工作,但我有一个问题VerifyInputWhenFocusTarget
财产。
我制作了一个标签来显示状态并被覆盖verify()
and 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;
}
}
}
这是测试应用程序的屏幕截图:
As can be seen, there are two buttons. One of them has the VerifyInputWhenFocusTarget
property set to true
and the other one has the same property set to false
but there isn't any difference when the button is clicked under the circumstance of false text field validation. The false validation can be provoked by entering negative number or some number with more than 4 decimal digits. The InputVerifier
indeed prevents transfering the focus to the other text field but it does not prevent activating the button. Since it does not make sense (at least not to me) the button could be activated without first getting the focus, there shouldn't be a possibility to activate the Start! button when the text field validation method verify()
returned false
.
EDIT:
I have now changed the code to accommodate the trashgod's suggestion (conditioning the enabled state of the Start! button with FocusListener
) and here is the working example:
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()
对于每个文本字段)。
当然,代码需要一些小的调整(制表符顺序、更新状态……),但这超出了本主题的范围。
结论:
Although VerifyInputWhenFocusTarget
property is apparently useful (here in the example the ? button can gain focus even in case of text field(s) validation was false
), I am still holding my opinion the documentation is not quite precise in describing all the important side effects which are rather counterintuitive so there is a need of doing further testing and investigations (perhaps analyzing the source code) besides just reading the docs.