Perform action upon completion of barcode scanning

2019-08-05 17:16发布

问题:

I have a JTextField barcodeTextField which accepts characters from the scanned barcode using a barcode scanner. From what I know, barcode scanning is like typing the characters very fast or copy-pasting the characters on the text field. barcodeTextField is also used to show suggestions and fill up others fields with information (just like searching in Google where suggestions are shown as you type).

So far I implemented this using DocumentListener:

barcodeTextField.getDocument().addDocumentListener(new DocumentListener() {
  public void set() {
    System.out.println("Pass");
    // Do a lot of things here like DB CRUD operations.
  }

  @Override
  public void removeUpdate(DocumentEvent arg0) {
    set();
  }

  @Override
  public void insertUpdate(DocumentEvent arg0) {
    set();
  }

  @Override
  public void changedUpdate(DocumentEvent arg0) {
    set();
  }
});

The problem is: If the barcode scanned has 13 characters, set() is executed 13 times, and so with DB operations. Same goes when I type "123" to show suggestion list, set() is executed 3 times.

I wanted set() to get executed when the user stops typing on barcodeTextField. In Javascript/JQuery, this can be done using the keyup() event and having setTimeout() method inside, clearTimeout() when user is still typing.

How to implement this behavior for JTextField in Java?

回答1:

"Is there any way to obtain the string entered on JTextField when the user stops typing"

The same way, Javascript has the timeout, Swing has a Timer. So if what you are looking for is achieved in Javscript using its "timer" fucntionality, you can see if you can get it working with a Swing Timers

For example Timer has a restart. So you can set a delay on the timer for say 1000 milliseconds. Once text is being typed (first change in the document), check if (timer.isRunning()), and timer.restart() if it is, else timer.start() (meaning first change in document). The action for the timer will only occur if one second passes after any document change. Any further changes occurring before the second is up, will cause the timer to reset. And set timer.setRepeats(false) so the action only occurs once

Your document listener might look something like

class TimerDocumentListener implements DocumentListener {

    private Document doc;
    private Timer timer;

    public TimerDocumentListener() {
        timer = new Timer(1000, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (doc != null) {
                    try {
                        String text = doc.getText(0, doc.getLength());
                        statusLabel.setText(text);
                    } catch (BadLocationException ex) {
                        Logger.getLogger(TimerDemo.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        });
        timer.setRepeats(false);
    }

    public void insertUpdate(DocumentEvent e) { set(e); }
    public void removeUpdate(DocumentEvent e) { set(e); }
    public void changedUpdate(DocumentEvent e) { set(e); }

    private void set(DocumentEvent e) {
        if (timer.isRunning()) {
            timer.restart();
        } else {
            this.doc = e.getDocument();
            timer.start();
        }
    }
}

Here's a complete example, where I "mock" typing, by explicitly inserting into the document (nine numbers) of the text field at a controlled interval of 500 milliseconds. You can see in the Timer owned by the DocumentListener that the delay is 1000 milliseconds. So as long as the typing occurs, the DocumentListener timer will not perform its action as the delay is longer than 500 milliseconds. For every change in the document, the timer restarts.

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

public class TimerDemo {

    private JTextField field;
    private JLabel statusLabel;

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            public void run() {
                new TimerDemo();
            }
        };
        SwingUtilities.invokeLater(runnable);
    }

    public TimerDemo() {
        JFrame frame = new JFrame();
        frame.setLayout(new GridLayout(0, 1));

        field = new JTextField(20);
        field.getDocument().addDocumentListener(new TimerDocumentListener());
        statusLabel = new JLabel(" ");

        JButton start = new JButton("Start Fake Typing");
        start.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                startInsertTimer();
            }
        });

        frame.add(field);
        frame.add(statusLabel);
        frame.add(start);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void startInsertTimer() {
        Timer timer = new Timer(500, new ActionListener() {
            private int count = 9;

            public void actionPerformed(ActionEvent e) {
                if (count == 0) {
                    ((Timer) e.getSource()).stop();
                } else {
                    Document doc = field.getDocument();
                    int length = doc.getLength();
                    try {
                        doc.insertString(length, Integer.toString(count), null);
                    } catch (BadLocationException ex) {
                        Logger.getLogger(TimerDemo.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    count--;
                }
            }
        });
        timer.start();
    }

    class TimerDocumentListener implements DocumentListener {

        private Document doc;
        private Timer timer;

        public TimerDocumentListener() {
            timer = new Timer(1000, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (doc != null) {
                        try {
                            String text = doc.getText(0, doc.getLength());
                            statusLabel.setText(text);
                        } catch (BadLocationException ex) {
                            Logger.getLogger(TimerDemo.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            });
            timer.setRepeats(false);
        }

        public void insertUpdate(DocumentEvent e) { set(e); }
        public void removeUpdate(DocumentEvent e) { set(e); }
        public void changedUpdate(DocumentEvent e) { set(e); }

        private void set(DocumentEvent e) {
            if (timer.isRunning()) {
                timer.restart();
            } else {
                this.doc = e.getDocument();
                timer.start();
            }
        }
    }
}