Value Change Listener menjadi JTextField

215

Saya ingin kotak pesan muncul segera setelah pengguna mengubah nilai di bidang teks. Saat ini, saya perlu menekan tombol enter untuk mengeluarkan kotak pesan. Apakah ada yang salah dengan kode saya?

textField.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent e) {

        if (Integer.parseInt(textField.getText())<=0){
            JOptionPane.showMessageDialog(null,
                    "Error: Please enter number bigger than 0", "Error Message",
                    JOptionPane.ERROR_MESSAGE);
        }       
    }
}

Bantuan apa pun akan dihargai!

pengguna236501
sumber

Jawaban:

373

Tambahkan pendengar ke Dokumen yang mendasarinya, yang secara otomatis dibuat untuk Anda.

// Listen for changes in the text
textField.getDocument().addDocumentListener(new DocumentListener() {
  public void changedUpdate(DocumentEvent e) {
    warn();
  }
  public void removeUpdate(DocumentEvent e) {
    warn();
  }
  public void insertUpdate(DocumentEvent e) {
    warn();
  }

  public void warn() {
     if (Integer.parseInt(textField.getText())<=0){
       JOptionPane.showMessageDialog(null,
          "Error: Please enter number bigger than 0", "Error Message",
          JOptionPane.ERROR_MESSAGE);
     }
  }
});
Codemwnci
sumber
format yang bagus untuk pemeran peringatan / jenis. Pola yang sama akan berguna untuk menangani jumlah ganda (angka penjualan / harga yang dimasukkan atau ditampilkan)
Max West
itu berfungsi dengan baik tetapi saya punya pertanyaan itu, ketika saya memasukkan beberapa teks di bidang teks maka saya ingin memanggil metode. saya tidak punya banyak ide tentang bagaimana hal itu dilakukan ..
Saya mengalami masalah dengan JTable yang tidak mendapatkan pembaruan kotak teks dari JComboBox yang dapat diedit ketika mengklik sel tabel lain, dan fungsi insertUpdate di sini adalah satu-satunya cara untuk membuatnya berfungsi dengan baik.
winchella
14
"Error Massage"
ungato
51

Jawaban yang biasa untuk ini adalah "gunakan a DocumentListener". Namun, saya selalu menemukan antarmuka yang rumit. Sejujurnya antarmuka over-engineered. Ini memiliki tiga metode, untuk penyisipan, penghapusan, dan penggantian teks, ketika hanya membutuhkan satu metode: penggantian. (Penyisipan dapat dilihat sebagai pengganti tanpa teks dengan beberapa teks, dan penghapusan dapat dilihat sebagai penggantian beberapa teks tanpa teks.)

Biasanya yang Anda inginkan adalah mengetahui kapan teks dalam kotak telah berubah , jadi ini tipikalDocumentListener implementasi memiliki tiga metode yang memanggil satu metode.

Karena itu saya membuat metode utilitas berikut, yang memungkinkan Anda menggunakan yang lebih sederhana ChangeListenerdaripada DocumentListener. (Menggunakan sintaks Java 8, tetapi Anda dapat mengadaptasinya untuk Java lama jika diperlukan.)

/**
 * Installs a listener to receive notification when the text of any
 * {@code JTextComponent} is changed. Internally, it installs a
 * {@link DocumentListener} on the text component's {@link Document},
 * and a {@link PropertyChangeListener} on the text component to detect
 * if the {@code Document} itself is replaced.
 * 
 * @param text any text component, such as a {@link JTextField}
 *        or {@link JTextArea}
 * @param changeListener a listener to receieve {@link ChangeEvent}s
 *        when the text is changed; the source object for the events
 *        will be the text component
 * @throws NullPointerException if either parameter is null
 */
public static void addChangeListener(JTextComponent text, ChangeListener changeListener) {
    Objects.requireNonNull(text);
    Objects.requireNonNull(changeListener);
    DocumentListener dl = new DocumentListener() {
        private int lastChange = 0, lastNotifiedChange = 0;

        @Override
        public void insertUpdate(DocumentEvent e) {
            changedUpdate(e);
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            changedUpdate(e);
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            lastChange++;
            SwingUtilities.invokeLater(() -> {
                if (lastNotifiedChange != lastChange) {
                    lastNotifiedChange = lastChange;
                    changeListener.stateChanged(new ChangeEvent(text));
                }
            });
        }
    };
    text.addPropertyChangeListener("document", (PropertyChangeEvent e) -> {
        Document d1 = (Document)e.getOldValue();
        Document d2 = (Document)e.getNewValue();
        if (d1 != null) d1.removeDocumentListener(dl);
        if (d2 != null) d2.addDocumentListener(dl);
        dl.changedUpdate(null);
    });
    Document d = text.getDocument();
    if (d != null) d.addDocumentListener(dl);
}

Berbeda dengan menambahkan pendengar langsung ke dokumen, ini menangani kasus (tidak umum) yang Anda instal objek dokumen baru pada komponen teks. Selain itu, ia bekerja di sekitar masalah yang disebutkan dalam jawaban Jean-Marc Astesana , di mana dokumen itu terkadang menembakkan lebih banyak peristiwa daripada yang diperlukan.

Bagaimanapun, metode ini memungkinkan Anda mengganti kode yang menjengkelkan yang terlihat seperti ini:

someTextBox.getDocument().addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) {
        doSomething();
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
        doSomething();
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
        doSomething();
    }
});

Dengan:

addChangeListener(someTextBox, e -> doSomething());

Kode dirilis ke domain publik. Selamat bersenang-senang!

Boann
sumber
5
Solusi serupa: buat abstract class DocumentChangeListener implements DocumentListenerdengan metode abstrak ekstra change(DocumentEvent e)yang Anda panggil dari ketiga metode lainnya. Tampak lebih jelas bagi saya karena menggunakan kurang lebih logika yang sama dengan abstract *Adapterpendengar.
geronimo
+1 sebagai changedUpdatemetode harus dipanggil secara eksplisit melalui panggilan dalam masing-masing insertUpdatedan removeUpdate, untuk membuatnya berfungsi ..
Kais
16

Cukup buat antarmuka yang memperluas DocumentListener dan mengimplementasikan semua metode DocumentListener:

@FunctionalInterface
public interface SimpleDocumentListener extends DocumentListener {
    void update(DocumentEvent e);

    @Override
    default void insertUpdate(DocumentEvent e) {
        update(e);
    }
    @Override
    default void removeUpdate(DocumentEvent e) {
        update(e);
    }
    @Override
    default void changedUpdate(DocumentEvent e) {
        update(e);
    }
}

lalu:

jTextField.getDocument().addDocumentListener(new SimpleDocumentListener() {
    @Override
    public void update(DocumentEvent e) {
        // Your code here
    }
});

atau Anda bahkan dapat menggunakan ekspresi lambda:

jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
    // Your code here
});
Andrey Megvinov
sumber
1
Jangan lupa bahwa solusi ini membutuhkan kelas abstrak alih-alih antarmuka di semua versi sebelum Java 8.
klaar
15

Sadarilah bahwa ketika pengguna memodifikasi bidang, DocumentListener dapat, kadang-kadang, menerima dua peristiwa. Misalnya, jika pengguna memilih seluruh konten bidang, lalu tekan tombol, Anda akan menerima removeUpdate (semua konten dihapus) dan insertUpdate. Dalam kasus Anda, saya pikir itu bukan masalah tetapi, secara umum, itu masalah. Sayangnya, sepertinya tidak ada cara untuk melacak konten textField tanpa mensubklasifikasikan JTextField. Berikut adalah kode dari kelas yang menyediakan properti "teks":

package net.yapbam.gui.widget;

import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

/** A JTextField with a property that maps its text.
 * <br>I've found no way to track efficiently the modifications of the text of a JTextField ... so I developed this widget.
 * <br>DocumentListeners are intended to do it, unfortunately, when a text is replace in a field, the listener receive two events:<ol>
 * <li>One when the replaced text is removed.</li>
 * <li>One when the replacing text is inserted</li>
 * </ul>
 * The first event is ... simply absolutely misleading, it corresponds to a value that the text never had.
 * <br>Anoter problem with DocumentListener is that you can't modify the text into it (it throws IllegalStateException).
 * <br><br>Another way was to use KeyListeners ... but some key events are throw a long time (probably the key auto-repeat interval)
 * after the key was released. And others events (for example a click on an OK button) may occurs before the listener is informed of the change.
 * <br><br>This widget guarantees that no "ghost" property change is thrown !
 * @author Jean-Marc Astesana
 * <BR>License : GPL v3
 */

public class CoolJTextField extends JTextField {
    private static final long serialVersionUID = 1L;

    public static final String TEXT_PROPERTY = "text";

    public CoolJTextField() {
        this(0);
    }

    public CoolJTextField(int nbColumns) {
        super("", nbColumns);
        this.setDocument(new MyDocument());
    }

    @SuppressWarnings("serial")
    private class MyDocument extends PlainDocument {
        private boolean ignoreEvents = false;

        @Override
        public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            String oldValue = CoolJTextField.this.getText();
            this.ignoreEvents = true;
            super.replace(offset, length, text, attrs);
            this.ignoreEvents = false;
            String newValue = CoolJTextField.this.getText();
            if (!oldValue.equals(newValue)) CoolJTextField.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue);
        }

        @Override
        public void remove(int offs, int len) throws BadLocationException {
            String oldValue = CoolJTextField.this.getText();
            super.remove(offs, len);
            String newValue = CoolJTextField.this.getText();
            if (!ignoreEvents && !oldValue.equals(newValue)) CoolJTextField.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue);
        }
    }
Jean-Marc Astesana
sumber
3
Swing sudah memiliki jenis textField yang memetakan perubahan dokumen ke properti - itu disebut JFormattedTextField :-)
kleopatra
11

Saya tahu ini berkaitan dengan masalah yang sangat lama, namun, itu menyebabkan saya juga mengalami masalah. Ketika kleopatra merespons dalam komentar di atas, saya memecahkan masalah dengan a JFormattedTextField. Namun, solusinya memerlukan sedikit lebih banyak pekerjaan, tetapi lebih rapi.

Tidak JFormattedTextFieldsecara default memicu perubahan properti setelah setiap teks berubah di bidang. Konstruktor default JFormattedTextFieldtidak membuat formatter.

Namun, untuk melakukan apa yang disarankan OP, Anda perlu menggunakan formatter yang akan memanggil commitEdit()metode setelah setiap pengeditan yang valid dari lapangan. The commitEdit()Metode inilah yang memicu perubahan properti dari apa yang saya lihat dan tanpa formatter, hal ini dipicu oleh default pada perubahan fokus atau ketika tombol enter ditekan.

Lihat http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value untuk rincian lebih lanjut.

Buat objek formatter ( DefaultFormatter) default untuk diteruskan ke JFormattedTextFieldbaik melalui konstruktornya atau metode penyetel. Salah satu metode formatter default adalah setCommitsOnValidEdit(boolean commit), yang mengatur formatter untuk memicu commitEdit()metode setiap kali teks diubah. Ini kemudian dapat diambil dengan menggunakan PropertyChangeListenerdan propertyChange()metode.

Astridax
sumber
2
textBoxName.getDocument().addDocumentListener(new DocumentListener() {
   @Override
   public void insertUpdate(DocumentEvent e) {
       onChange();
   }

   @Override
   public void removeUpdate(DocumentEvent e) {
      onChange();
   }

   @Override
   public void changedUpdate(DocumentEvent e) {
      onChange();
   } 
});

Tapi saya tidak akan hanya menguraikan apa pun yang pengguna (mungkin tidak sengaja) sentuh pada keyboardnya menjadi Integer. Anda harus menangkap ada Exceptionyang terlempar dan pastikan JTextFieldtidak kosong.

DerBobby
sumber
2

Jika kita menggunakan metode runnable, SwingUtilities.invokeLater () saat menggunakan aplikasi pendengar dokumen terkadang macet dan meluangkan waktu untuk memperbarui hasilnya (sesuai eksperimen saya). Selain itu kami juga dapat menggunakan acara KeyReleased untuk pendengar perubahan bidang teks seperti yang disebutkan di sini .

usernameTextField.addKeyListener(new KeyAdapter() {
    public void keyReleased(KeyEvent e) {
        JTextField textField = (JTextField) e.getSource();
        String text = textField.getText();
        textField.setText(text.toUpperCase());
    }
});
Kakumanu siva krishna
sumber
1

itu adalah versi pembaruan Codemwnci. kodenya cukup baik dan berfungsi baik kecuali pesan kesalahan. Untuk menghindari kesalahan, Anda harus mengubah pernyataan kondisi.

  // Listen for changes in the text
textField.getDocument().addDocumentListener(new DocumentListener() {
  public void changedUpdate(DocumentEvent e) {
    warn();
  }
  public void removeUpdate(DocumentEvent e) {
    warn();
  }
  public void insertUpdate(DocumentEvent e) {
    warn();
  }

  public void warn() {
     if (textField.getText().length()>0){
       JOptionPane.showMessageDialog(null,
          "Error: Please enter number bigger than 0", "Error Massage",
          JOptionPane.ERROR_MESSAGE);
     }
  }
});
Bipul Chandra Dev Nath
sumber
Adaptasi Anda mengaktifkan dialog pesan kesalahan setiap kali string yang lebih panjang dari = 0 dimasukkan dalam bidang teks. Jadi itu pada dasarnya string apa pun selain string kosong. Itu bukan solusi yang diminta.
klaar
0

Anda bahkan dapat menggunakan "MouseExited" untuk mengontrol. contoh:

 private void jtSoMauMouseExited(java.awt.event.MouseEvent evt) {                                    
        // TODO add your handling code here:
        try {
            if (Integer.parseInt(jtSoMau.getText()) > 1) {
                //auto update field
                SoMau = Integer.parseInt(jtSoMau.getText());
                int result = SoMau / 5;

                jtSoBlockQuan.setText(String.valueOf(result));
            }
        } catch (Exception e) {

        }

    }   
fishgold192
sumber
6
tidak juga: persyaratannya adalah melakukan sesuatu ketika teksnya diubah - itu tidak terkait dengan mouseEvents ;-)
kleopatra
0

Saya baru mengenal WindowBuilder, dan, pada kenyataannya, baru kembali ke Jawa setelah beberapa tahun, tetapi saya menerapkan "sesuatu", kemudian berpikir saya akan mencarinya dan menemukan utas ini.

Saya di tengah menguji ini, jadi, berdasarkan menjadi baru untuk semua ini, saya yakin saya pasti kehilangan sesuatu.

Inilah yang saya lakukan, di mana "runTxt" adalah kotak teks dan "runName" adalah anggota data kelas:

public void focusGained(FocusEvent e) {
    if (e.getSource() == runTxt) {
        System.out.println("runTxt got focus");
        runTxt.selectAll();
    }
}

public void focusLost(FocusEvent e) {
    if (e.getSource() == runTxt) {
        System.out.println("runTxt lost focus");
        if(!runTxt.getText().equals(runName))runName= runTxt.getText();
        System.out.println("runText.getText()= " + runTxt.getText() + "; runName= " + runName);
    }
}

Tampaknya jauh lebih sederhana daripada apa yang ada di sini sejauh ini, dan tampaknya berhasil, tetapi, karena saya sedang menulis ini, saya sangat menghargai mendengar ada gotcha yang diabaikan. Apakah ini masalah dimana pengguna dapat masuk & meninggalkan kotak teks tanpa membuat perubahan? Saya pikir semua yang Anda lakukan adalah tugas yang tidak perlu.

Manusia Roket
sumber
-1

Gunakan KeyListener (yang memicu pada sembarang tombol) daripada ActionListener (yang terpicu saat masuk)

nick
sumber
Ini tidak berfungsi karena nilai bidang tidak ditangkap dengan benar, field.getText()mengembalikan nilai awal. dan event ( arg0.getKeyChar()) mengembalikan pemeriksaan kesalahan tombol ditekan diperlukan untuk menentukan apakah Anda harus menyatukan dengan teks bidang.
glend
@ glend, Anda dapat menggunakan event keyReleased alih-alih event keyTyped. Itu bekerja untuk saya dan mendapatkan nilai lengkap.
Kakumanu siva krishna
-1

DocumentFilter ? Ini memberi Anda kemampuan untuk memanipulasi.

[ http://www.java2s.com/Tutorial/Java/0240__Swing/FormatJTextFieldstexttouppercase.htm ]

Maaf. Saya menggunakan Jython (Python di Jawa) - tetapi mudah dimengerti

# python style
# upper chars [ text.upper() ]

class myComboBoxEditorDocumentFilter( DocumentFilter ):
def __init__(self,jtext):
    self._jtext = jtext

def insertString(self,FilterBypass_fb, offset, text, AttributeSet_attrs):
    txt = self._jtext.getText()
    print('DocumentFilter-insertString:',offset,text,'old:',txt)
    FilterBypass_fb.insertString(offset, text.upper(), AttributeSet_attrs)

def replace(self,FilterBypass_fb, offset, length, text, AttributeSet_attrs):
    txt = self._jtext.getText()
    print('DocumentFilter-replace:',offset, length, text,'old:',txt)
    FilterBypass_fb.replace(offset, length, text.upper(), AttributeSet_attrs)

def remove(self,FilterBypass_fb, offset, length):
    txt = self._jtext.getText()
    print('DocumentFilter-remove:',offset, length, 'old:',txt)
    FilterBypass_fb.remove(offset, length)

// (java style ~example for ComboBox-jTextField)
cb = new ComboBox();
cb.setEditable( true );
cbEditor = cb.getEditor();
cbEditorComp = cbEditor.getEditorComponent();
cbEditorComp.getDocument().setDocumentFilter(new myComboBoxEditorDocumentFilter(cbEditorComp));
papuga
sumber