Primo: tutti implementazioni di InputVerifier che aprono la finestra di dialogo nella verifica() sono validi . Hanno violato il loro contratto, doc dell'API:
Questo metodo non dovrebbe avere effetti collaterali.
con il "dovrebbe" che realmente significa "non deve". Il posto giusto per gli effetti collaterali è shouldYieldFocus.
Secondo: spostare l'effetto collaterale (che mostra la finestra di messaggio) correttamente nella shouldYieldFocus non funziona così ... a causa di una bug (THEY call it feature request ;-), che è più vecchio di un decennio e in the top 10 RFEs
Essendo un hack- attorno a un bug, mouseListener @ di dareurdrem è buono come qualsiasi mod praticabile può ottenere :-)
Aggiornamento
Dopo aver giocato un po 'con diverse opzioni per hack in giro il bug, ecco un altro trucco - è come fragili come tutti gli hack ar e (e non sopravvive una ginocchiera LAF, deve essere reinstallato se è richiesto toggling dinamico)
Per hacking del comportamento del mouse l'approccio di base è quello di collegare in all'ascoltatore installato dal ui:
- trovare l'originale
- implementare un listener personalizzato che delega la maggior parte degli eventi direttamente al originale
- per gli eventi pressati richiesta concentrarsi prima: se ceduto delegato originale, se non fare nulla
L'ultimo punto è leggermente più coinvolto perché gli eventi di messa a fuoco possono essere asincroni, quindi dobbiamo invocare il controllo per essere focalizzati. L'invocazione, a sua volta, richiede l'invio di una liberatoria nel caso nessuno si opponga.
Un'altra stranezza è l'azione urgente di rootPane (per il suo defaultButton): è fatta senza rispettare alcun inputVerifiers chiamando incondizionatamente doClick. Ciò può essere violato agganciando in azione, seguendo lo stesso schema aggancio nella mouseListener:
- trovare l'azione premuto del rootPane
- attuare un'azione personalizzata, che verifica la presenza di un inputVerifier potenzialmente veto: conferire al originale se no, non fare nulla altrimenti
l'esempio modificato in questo senso:
public class VerifierTest implements Runnable {
private static final long serialVersionUID = 1L;
@Override
public void run() {
InteractiveTestCase.setLAF("Win");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
JTextField tf = new JTextField("TextField1");
tf.setInputVerifier(new PassVerifier());
frame.add(tf, BorderLayout.NORTH);
final JButton b = new JButton("Button");
frame.add(b);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked");
}
});
// hook into the mouse listener
replaceBasicButtonListener(b);
frame.add(new JTextField("not validating, something else to focus"),
BorderLayout.SOUTH);
frame.getRootPane().setDefaultButton(b);
// hook into the default button action
Action pressDefault = frame.getRootPane().getActionMap().get("press");
frame.getRootPane().getActionMap().put("press", new DefaultButtonAction(pressDefault));
frame.setVisible(true);
}
protected void replaceBasicButtonListener(AbstractButton b) {
final BasicButtonListener original = getButtonListener(b);
if (original == null) return;
Hacker l = new Hacker(original);
b.removeMouseListener(original);
b.addMouseListener(l);
}
public static class Hacker implements MouseListener {
private BasicButtonListener original;
/**
* @param original the listener to delegate to.
*/
public Hacker(BasicButtonListener original) {
this.original = original;
}
/**
* Hook into the mousePressed: first request focus and
* check its success before handling it.
*/
@Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
if(e.getComponent().contains(e.getX(), e.getY())) {
// check if we can get the focus
e.getComponent().requestFocus();
invokeHandleEvent(e);
return;
}
}
original.mousePressed(e);
}
/**
* Handle the pressed only if we are focusOwner.
*/
protected void handlePressed(final MouseEvent e) {
if (!e.getComponent().hasFocus()) {
// something vetoed the focus transfer
// do nothing
return;
} else {
original.mousePressed(e);
// need a fake released now: the one from the
// original cycle might never has reached us
MouseEvent released = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_RELEASED,
e.getWhen(), e.getModifiers(),
e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
);
original.mouseReleased(released);
}
}
/**
* focus requests might be handled
* asynchronously. So wrap the check
* wrap the block into an invokeLater.
*/
protected void invokeHandleEvent(final MouseEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
handlePressed(e);
}
});
}
@Override
public void mouseClicked(MouseEvent e) {
original.mouseClicked(e);
}
@Override
public void mouseReleased(MouseEvent e) {
original.mouseReleased(e);
}
@Override
public void mouseEntered(MouseEvent e) {
original.mouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
original.mouseExited(e);
}
}
public static class DefaultButtonAction extends AbstractAction {
private Action original;
/**
* @param original
*/
public DefaultButtonAction(Action original) {
this.original = original;
}
@Override
public void actionPerformed(ActionEvent e) {
JRootPane root = (JRootPane) e.getSource();
JButton owner = root.getDefaultButton();
if (owner != null && owner.getVerifyInputWhenFocusTarget()) {
Component c = KeyboardFocusManager
.getCurrentKeyboardFocusManager()
.getFocusOwner();
if (c instanceof JComponent && ((JComponent) c).getInputVerifier() != null) {
if (!((JComponent) c).getInputVerifier().shouldYieldFocus((JComponent) c)) return;
}
}
original.actionPerformed(e);
}
}
/**
* Returns the ButtonListener for the passed in Button, or null if one
* could not be found.
*/
private BasicButtonListener getButtonListener(AbstractButton b) {
MouseMotionListener[] listeners = b.getMouseMotionListeners();
if (listeners != null) {
for (MouseMotionListener listener : listeners) {
if (listener instanceof BasicButtonListener) {
return (BasicButtonListener) listener;
}
}
}
return null;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new VerifierTest());
}
public static class PassVerifier extends InputVerifier {
/**
* Decide whether or not the input is valid without
* side-effects.
*/
@Override
public boolean verify(JComponent input) {
final JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
return false;
}
/**
* Implemented to ask the user what to do if the input isn't valid.
* Note: not necessarily the best usability, it's mainly to
* demonstrate the different effects on not/agreeing with
* yielding focus transfer.
*/
@Override
public boolean shouldYieldFocus(final JComponent input) {
boolean valid = super.shouldYieldFocus(input);
if (!valid) {
String message = "illegal value: " + ((JTextField) input).getText();
int goAnyWay = JOptionPane.showConfirmDialog(input, "invalid value: " +
message + " - go ahead anyway?");
valid = goAnyWay == JOptionPane.OK_OPTION;
}
return valid;
}
}
}
Per favore, prova a racchiudere la chiamata showMessageDialog in Runnable e assegnala a SwingUtilities :: invokeLater (Runnable) – gd1
@ gd14 Ciao, ho provato l'approccio che hai affermato ma non sembra funzionare. Il codice modificato è il seguente: - string Messaggio finale = "valore non valido:" + tf.getText(); \t javax.swing.SwingUtilities.invokeLater (new Runnable() { \t \t public void run() { \t \t \t JOptionPane.showMessageDialog (null, messaggio, \t \t \t \t \t "valore non valido", JOptionPane.ERROR_MESSAGE \t \t} \t}); \t return false; – dareurdream
Capisco. Qual è la tua versione del sistema operativo e di Java? Sono su OSX con Java 1.6 e funziona perfettamente. – gd1