下面的代碼只顯示了一個文本欄位和一個按鈕。
文本欄位有一個不接受空欄位的 inputVerifier。
只要驗證器的“假”結果由 optionPane 發出信號,按鈕的背景在關閉 optionPane 后變為灰色并在 mouseOver 上變為“按下”(并且僅在單擊按鈕時;如果文本欄位與 tab 鍵一起離開則不會)。
現在洗掉按鈕的 MouseListener 的注釋斜線并再次運行程式。一旦關閉 optionPane,您將看到按鈕回傳到其常規背景。
此解決方案在許多情況下都有效,但在涉及超出 SSCCE 范圍的實際程式時,它并非免于不穩定。對于不穩定,我的意思是有時一切都按預期作業,有時雖然 inputVerifier 發出錯誤信號并回傳“false”,但焦點從 textField 中釋放,因此丟失的輸入被接受。我認為這是由于 MouseListener 中的 invokeLater 造成的。
我可以將我當前的實際代碼減少到最低限度來演示這個問題,但我擔心我最終會得到幾頁代碼。所以我首先想問一下,是否有人已經處理過這個問題并可以給出提示。謝謝。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonBackground2 extends JFrame {
public ButtonBackground2() {
setSize(350, 200);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
JPanel p= new JPanel();
JTextField tf = new JTextField();
tf.setPreferredSize(new Dimension(100, 20));
tf.setInputVerifier(new NonEmptyVerifier());
p.add(tf);
add(p, BorderLayout.CENTER);
p= new JPanel();
JButton btn = new JButton("Button");
btn.setPreferredSize(new Dimension(80, 30));
// btn.addMouseListener(new BtnBackgroundListener());
p.add(btn);
add(p, BorderLayout.SOUTH);
setVisible(true);
}
public static void main(String arg[]) {
EventQueue.invokeLater(ButtonBackground2::new);
}
class NonEmptyVerifier extends InputVerifier {
/*
public boolean shouldYieldFocus(JComponent source, JComponent target) {
return verify(source);
}
*/
public boolean verify(final JComponent input) {
JTextField tf = (JTextField) input;
if (tf.getText().trim().length()>0) {
System.out.println("OK");
return true;
}
JOptionPane.showMessageDialog(ButtonBackground2.this,
"Enter at least one character.",
"Missing input", JOptionPane.ERROR_MESSAGE);
return false;
}
}
class BtnBackgroundListener extends MouseAdapter {
public void mousePressed(final MouseEvent e) {
SwingUtilities.invokeLater(() -> {
JButton btn= (JButton)e.getSource();
if (!btn.hasFocus()) btn.getModel().setPressed(false);
});
}
}
}
編輯
令人驚訝的是,我可以將我的實際代碼減少到一小部分來展示不當行為。
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import javax.swing.*;
public class Y extends JFrame {
public static final long serialVersionUID = 100L;
public Y() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 240);
setLocationRelativeTo(null);
add(createTextFieldPanel(), BorderLayout.CENTER);
JButton bOK= new JButton("OK");
bOK.addActionListener(e -> System.out.println("OK, input accepted."));
/* Adding the following listener makes in case of erroneous input the focus
locking of the textfield's InputVerifier shaky. The InputVerifier itself,
however, works alright, as one sees from the unfailingly displayed error
message.
*/
bOK.addMouseListener(new BtnBackgroundListener());
add(bOK, BorderLayout.SOUTH);
setVisible(true);
}
static public void main(String args[]) {
EventQueue.invokeLater(Y::new);
}
private JPanel createTextFieldPanel() {
JPanel p= new JPanel(new FlowLayout(FlowLayout.LEFT));
p.add(new JLabel("Input:"));
MyTextField tf= new MyTextField(this);
tf.setPreferredSize(new Dimension(95, 20));
tf.setFont(new Font("Monospaced", Font.PLAIN, 13));
p.add(tf);
return p;
}
}
-----------------------------------------------------------
import java.awt.*;
import javax.swing.*;
public class MyTextField extends JTextField {
public static final long serialVersionUID = 50161L;
Component parent;
public MyTextField(Component parent) {
this.parent= parent;
setInputVerifier(new InputVerifier() {
/*
public boolean shouldYieldFocus(JComponent source, JComponent target) {
return verify(source);
}
*/
public boolean verify(JComponent comp) {
if (getText().equals("pass")) return true;
JOptionPane.showMessageDialog(parent,
"Input does not match the requested format.\n" getText(),
"Input error", JOptionPane.ERROR_MESSAGE);
return false;
}
});
}
}
因此,首先我們可以說 Camickr 懷疑代碼的長度/復雜性是否會產生任何影響是正確的。
其次,在這個演示中,洗掉 MouseListener 也會阻止焦點被不當釋放。
那么為什么程式ButtonBackground2有時只作業而程式Y呢?有時在第一次單擊按鈕時會接受不正確的輸入,有時必須多次重復單擊。
順便說一句,我正在運行 jdk 18,構建 18 36-2087。
uj5u.com熱心網友回復:
我也可以重現您在 Java 8 中看到的內容。這個答案的其余部分將適用于 Java 8。
問題在于實施BtnBackgroundListener。
JButton在類中創建的Y(即參考bOK)使用 a DefaultButtonModelwhich is a ButtonModel。但通常任何AbstractButton使用ButtonModel實體。
根據ButtonModel介面的檔案:
...在常規按鈕上按下和釋放滑鼠會觸發按鈕并導致 ActionEvent 被觸發。
考慮以下代碼:
import javax.swing.JButton;
import javax.swing.SwingUtilities;
public class Test {
private static void runExperiment() {
final JButton button = new JButton("Test");
button.addActionListener(e -> System.out.println("Action!"));
button.getModel().setArmed(true);
button.getModel().setPressed(true);
System.out.println("Before release...");
button.getModel().setPressed(false);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Test::runExperiment);
}
}
如果您運行它,您將看到以下輸出:
發布前... 行動!
然后程式將終止。這表明當釋放按鈕模型時(在它被按下 武裝之后),然后ActionEvent在給定的ActionListener. 在您的代碼中,在(但我稍后會注意到)setPressed(false)的實作中呼叫了。BtnBackgroundListenerinvokeLater
那么誰在事先打電話(這是啟動按下狀態所必需的)setArmed(true)?setPressed(true)根據來源或一個簡單的實驗(例如System.out.println(BasicButtonUI.class.isInstance(button.getUI()));),可以看到ButtonUI按鈕上的安裝是型別的(子類)BasicButtonUI,這反過來又安裝了默認值MouseListener,它可以做你可以想象的事情:它使按鈕功能正常當用戶在按鈕范圍內單擊滑鼠時,將模型的狀態更改為武裝并按下。它還支持翻轉效果、釋放和其他東西,但這些與問題無關。
BtnBackgroundListener也是一個MouseListener,它也安裝在按鈕上(與 UI 安裝的默認按鈕一起)。因此,當您單擊按鈕時,兩個MouseListeners 都會被呼叫(note MouseListeners 也適用于當前沒有焦點的組件)。因此代碼在內部按順序呼叫所有MouseListeners,但按什么順序并不重要,因為通過呼叫setPressed(false)內部SwingUtilities#invokeLater方法,您可以確保模型的釋放將在所有MouseListeners 被呼叫后發生。因此,默認設定MouseListener首先將按鈕設定為武裝并按下,一段時間后您釋放模型,該模型依次觸發ActionEvent每個模型ActionListener(包括接受輸入的模型)。
打電話給你MouseListener總是發生。盡管在你的模型中發布模型MouseListener并不總是觸發一個ActionEvent,我現在想不出一個可能的解釋。
為了防止這種...
不要使用 aMouseListener來監聽按鈕上的動作事件。這整個邏輯已經實作了,你只需要ActionListener單獨注冊一個。
由于您想使用InputVerifierthen 我建議在按鈕的動作偵聽器上傳遞一個標志(來自InputVerifier),這將指示輸入的健全性。例如:
import java.awt.GridLayout;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Main {
private static class MyInputVerifier extends InputVerifier {
private boolean validInput = false;
@Override
public boolean verify(final JComponent input) {
validInput = ((JTextField) input).getText().equals("pass");
return validInput;
}
}
private static void createAndShowGUI() {
final JTextField field1 = new JTextField(12),
field2 = new JTextField("Anything");
final JButton accept = new JButton("Submit");
final MyInputVerifier miv = new MyInputVerifier();
field1.setInputVerifier(miv);
accept.addActionListener(e -> {
if (miv.validInput)
System.out.println("Accepted!");
else
JOptionPane.showMessageDialog(accept, "Invalid input!");
});
final JPanel form = new JPanel(new GridLayout(0, 1));
form.add(field1);
form.add(field2);
form.add(accept);
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(form);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}
uj5u.com熱心網友回復:
此建議基于 gthanop 的建議,并結合了 OP 在驗證失敗時重置按鈕模型狀態的方法:
import java.awt.GridLayout;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Main5 {
private static class MyInputVerifier extends InputVerifier {
private boolean validInput = false;
private JButton button;
public MyInputVerifier (JButton button)
{
this.button = button;
}
@Override
public boolean verify(final JComponent input)
{
validInput = ((JTextField) input).getText().equals("pass");
if (!validInput)
{
JOptionPane.showMessageDialog(input, "Verifier detected invalid input!");
button.getModel().setPressed(false);
}
return validInput;
}
}
private static void createAndShowGUI() {
final JTextField field1 = new JTextField(12),
field2 = new JTextField("Anything");
final JButton accept = new JButton("Submit");
final MyInputVerifier miv = new MyInputVerifier(accept);
field1.setInputVerifier(miv);
accept.addActionListener(e -> {
if (miv.validInput)
System.out.println("Accepted!");
// else
// JOptionPane.showMessageDialog(accept, "Invalid input!");
});
final JPanel form = new JPanel(new GridLayout(0, 1));
form.add(field1);
form.add(field2);
form.add(accept);
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(form);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main5::createAndShowGUI);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/491732.html
