2016-07-31 33 views
2

Ich versuche, die Textfeld Benutzereingaben mit javax.swing.InputVerifier zu validieren und die Eingabe Validierung funktioniert wie erwartet, aber ich habe ein Problem in Bezug auf VerifyInputWhenFocusTarget Eigenschaft.VerifyInputWhenFocusTarget -Eigenschaft hat keine Wirkung

Ich habe ein Etikett gemacht, um den Status anzuzeigen und überschrieben verify() und shouldYieldFocus() Methoden der InputVerifier Unterklasse und das funktioniert gut.

Der nächste Schritt, den ich tun wollte, war, die VerifyInputWhenFocusTarget der Schaltfläche festzulegen, damit es den Fokus nicht erhalten würde, falls die Überprüfung des aktuellen Fokusbesitzers falsch war, aber ich bemerkte keinen Effekt der Einstellung der VerifyInputWhenFocusTarget Eigenschaft auf true und die Schaltfläche konnte gedrückt werden, auch wenn die Validierung des aktuellen Fokus Eigentümer falsch war.

Vielleicht verstehe ich nicht die Dokumente - ich dachte, die Einstellung der VerifyInputWhenFocusTarget Eigenschaft der Schaltfläche true würde verhindern, dass die Schaltfläche den Fokus erhalten, wenn unter den Umständen der falschen Validierung des Textfelds geklickt wird. Außerdem verstehe ich (falsch), wenn der Button den Fokus nicht bekommen konnte, würde seine actionPerformed() Methode nicht aufgerufen werden.

Die Schaltfläche kann jedoch angeklickt werden und ihre actionPerformed() Methode wird trotzdem von der falschen Validierung der Textfeld (e) 'bewacht' von javax.swing.InputVerifier ausgeführt. Hier

ist das abgestreifte Code:

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; 
     } 
    } 

} 

Und hier ist der Screenshot der Testanwendung:

InputVerifier Test screenshot

Wie man sehen kann, gibt es zwei Tasten. Einer von ihnen hat die VerifyInputWhenFocusTarget -Eigenschaft auf true und die andere hat die gleiche Eigenschaft auf false gesetzt, aber es gibt keinen Unterschied, wenn die Schaltfläche unter der Bedingung der falschen Textfeldvalidierung geklickt wird. Die falsche Validierung kann durch Eingabe einer negativen Zahl oder einer Zahl mit mehr als 4 Dezimalziffern provoziert werden. Die InputVerifier verhindert zwar, dass der Fokus auf das andere Textfeld übertragen wird, verhindert aber nicht die Aktivierung der Schaltfläche. Da es (zumindest für mich) keinen Sinn macht, könnte die Taste aktiviert werden, ohne zuerst den Fokus zu bekommen, es sollte keine Möglichkeit geben, das Start zu aktivieren! Schaltfläche, wenn die Textfeldvalidierungsmethode verify() zurückgegeben false.


EDIT:

Ich habe jetzt den Code verändern die trashgod Vorschlag (Konditionierung des aktivierten Zustand der starten Taste mit FocusListener!) Aufzunehmen, und hier ist das Arbeitsbeispiel:

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); 
     } 
    } 
} 

Ich habe den Code der verify() Methode leicht geändert, um den Fokus zu ändern, wenn nichts eingegeben wird (focusLost() Methode überprüft, ob alle Textfelder einige Eingaben enthalten und überprüft auch, ob die Eingaben gültig sind, indem für jedes der Textfelder explizit verify() aufgerufen wird).

Der Code benötigt natürlich einige kleinere Optimierungen (Tab-Reihenfolge, Aktualisierung des Status, ...), aber das ist nicht im Rahmen dieses Themas.

Fazit:

Obwohl VerifyInputWhenFocusTarget Eigenschaft offenbar nützlich ist (? Hier im Beispiel der Taste Fokus auch bei Textfeld gewinnen kann (s) Validierung war false), ich bin immer noch mit angehaltenem Meinung die Dokumentation ist nicht ganz genau in der Beschreibung aller wichtigen Nebenwirkungen, die eher nicht intuitiv sind, so dass es notwendig ist, weitere Tests und Untersuchungen (vielleicht die Analyse des Quellcodes) neben nur das Lesen der Dokumente.

Antwort

2

Ihre Anrufe setVerifyInputWhenFocusTarget() zu tun haben einen Effekt-genau die Wirkung der Bestimmung „ob [die] Eingangs Verifizierer für den aktuellen Fokus Besitzer wird vor konzentrieren sich diese Komponente Anfragen genannt werden.“ [Hervorhebung von mir] Insbesondere begründen den folgenden Zustand:

  • Lassen statusText seinen Anfangswert haben, oder den Anfangswert wiederherstellen, indem Sie einen gültigen Wert eingeben.

  • Lassen Sie tfFirstNum oder tfSecondNum Fokus haben, während sie einen ungültigen Wert enthalten.

beobachten dann, dass

  • Ein Klick auf ? verlässt statusText unverändert, was bedeutet, dass die Eingangs Verifizierer die fokussierte Komponente nicht genannt wurde, wie durch

    btnHelp.setVerifyInputWhenFocusTarget(false); 
    
  • Ein Klick auf Sätze beginnen vorgeschriebenen statusText einen Fehler zu reflektieren, was bedeutet, dass die Eingangs Verifizierer die fokussierte Komponente war genannt, wie vorgeschrieben durch

    btnStart.setVerifyInputWhenFocusTarget(true); 
    

Beachten Sie, dass beide Bedingungen erfüllt sein müssen, um den Effekt zu sehen, wenn eine der beiden Schaltflächen geklickt wird.

+0

Klicken Sie auf "Start!" setzt 'statusText', um einen Fehler zu reflektieren, was bedeutet, dass der Eingabefilter der fokussierten Komponente tatsächlich aufgerufen wurde - aber falsche valitation hat ** nicht ** verhindert, dass die Schaltfläche den Fokus erhält/aktiviert wird. Auf der anderen Seite verhindert die falsche Validierung ** den Fokus im Fall eines Textfelds. Ich finde ein solches Verhalten inkonsistent, aber was noch wichtiger ist, wäre die Verwendung des 'InputVerifiers', wenn ** alle ** der Textfelder ** noch einmal ** validiert werden müssen, um die Schaltfläche zu aktivieren/deaktivieren oder um den Code innerhalb der Schaltfläche "actionPerformed" zu überspringen? –

+0

Aus den Oracle-Dokumenten für 'setVerifyInputWhenFocusTarget()': 'Auf Komponenten wie eine Cancel-Schaltfläche oder eine Bildlaufleiste auf false setzen **, die auch dann aktiviert werden sollte, wenn die Eingabe im aktuellen Fokuseigner nicht vom Eingabeverifizierer "übergeben" wird ** für diese Komponente.Wenn dies der Zweck ist, dann ist das Gegenteil der Fall: "Setzen Sie auf ** ** ** ** ** auf Komponenten wie ** Start ** -Schaltfläche, die ** nicht ** aktiviert werden soll, wenn die Eingabe im aktuellen Fokus-Besitzer nicht ist vom Input-Verifier für diese Komponente "übergeben". Aber "das Gegenteil" (auf "wahr" gesetzt) ​​verhindert ** nicht, dass die Schaltfläche aktiviert wird. –

+1

Verwenden Sie einen separaten ['FocusListener'] (https://docs.oracle.com/javase/8/docs/api/java/awt/event/FocusListener.html), um den aktivierten Zustand von' btnStart' zu aktivieren; Sie können 'txtVerifier.verify()' wie erforderlich aufrufen, um den korrekten Status zu ermitteln. – trashgod