JTextField için Değer Değişim Dinleyicisi


215

Kullanıcı metin alanındaki değeri değiştirdikten hemen sonra ileti kutusunun görünmesini istiyorum. Şu anda, mesaj kutusunun çıkması için enter tuşuna basmam gerekiyor. Kodumda bir sorun mu var?

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

Herhangi bir yardım mutluluk duyacağız!

Yanıtlar:


373

Sizin için otomatik olarak oluşturulan temel Belgeye bir dinleyici ekleyin.

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

uyarı / yayın türü için iyi bir biçim. Aynı model, çift miktarları işlemek için de yararlı olacaktır (satış rakamları / girilen veya görüntülenen fiyatlar)
Max West

gayet iyi çalışıyor ama ben metin alanına bazı metin eklemek o zaman ben bir yöntemi aramak istiyorum bir sorgu var. nasıl yapıldığı hakkında fazla bir fikrim yok ..

Başka bir tablo hücresini tıklattığınızda düzenlenebilir bir JComboBox metin kutusu güncelleştirmelerini almayan bir JTable ile ilgili sorunlar yaşıyordu ve burada insertUpdate işlevi düzgün çalışması için tek yoluydu.
winchella

14
"Hata Masajı"
ungato

51

Bunun olağan cevabı "kullan DocumentListener" dır . Ancak, her zaman bu arayüzü hantal buluyorum. Gerçekten arayüz aşırı tasarlanmış. Yalnızca bir yönteme ihtiyaç duyduğunda metnin eklenmesi, çıkarılması ve değiştirilmesi için üç yöntemi vardır: değiştirme. (Ekleme, metin içermeyen bir metnin yerine geçmesi olarak, kaldırma ise metin içermeyen bir metnin yerine geçmesi olarak görülebilir.)

Genellikle bilmek istediğiniz tek şey kutudaki metnin ne zaman değiştiğidir , bu nedenle tipik bir DocumentListeneruygulamada bir yöntemi çağıran üç yöntem vardır.

Bu nedenle, a ChangeListeneryerine daha basit kullanmanızı sağlayan aşağıdaki yardımcı programı yaptım DocumentListener. (Java 8'in lambda sözdizimini kullanır, ancak gerekirse eski Java için uyarlayabilirsiniz.)

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

Belgeye doğrudan bir dinleyici eklemenin aksine, bu, bir metin bileşenine yeni bir belge nesnesi yüklediğiniz (nadir) vakayı işler. Buna ek olarak, Jean-Marc Astesana'nın cevabında belirtilen sorunun etrafında çalışır ve belgenin bazen ihtiyaç duyduğundan daha fazla olayı tetikler .

Her neyse, bu yöntem aşağıdaki gibi can sıkıcı kodu değiştirmenizi sağlar:

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

İle:

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

Kod kamu malı olarak yayınlandı. İyi eğlenceler!


5
Benzer çözüm: diğer 3 yöntemden çağırdığınız abstract class DocumentChangeListener implements DocumentListenerekstra bir soyut change(DocumentEvent e)yöntemle bir oluşturun. abstract *AdapterDinleyicilerle az çok aynı mantığı kullandığı için bana daha açık görünüyor .
geronimo

changedUpdateYöntem olarak +1, her birinin içindeki bir çağrı yoluyla açık bir şekilde çağrılır insertUpdateve removeUpdateçalışmasını sağlamak için ..
Kais

16

Sadece DocumentListener'ı genişleten ve tüm DocumentListener yöntemlerini uygulayan bir arayüz oluşturun:

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

ve sonra:

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

veya lambda ifadesini bile kullanabilirsiniz:

jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
    // Your code here
});

1
Bu çözümün Java 8'den önceki tüm sürümlerde bir arayüz yerine soyut bir sınıf gerektirdiğini unutmayın.
Klaar

15

Kullanıcı alanı değiştirdiğinde, DocumentListener'ın bazen iki olay alabileceğini unutmayın. Örneğin, kullanıcı tüm alan içeriğini seçip bir tuşa basarsa, bir removeUpdate (tüm içerik kaldırılır) ve bir insertUpdate alırsınız. Sizin durumunuzda bunun bir sorun olduğunu düşünmüyorum ama genel olarak konuşursak. Ne yazık ki, JTextField alt sınıflaması olmadan textField içeriğini izlemek için hiçbir yolu yok gibi görünüyor. "Text" özelliği sağlayan bir sınıfın kodu:

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

3
Zaten Salıncak sahip bir mülke belge değişiklikleri eşler TextField türüdür - bu :-) JFormattedTextField denir
kleopatra

11

Bunun gerçekten eski bir sorunla ilgili olduğunu biliyorum, ancak bana da bazı sorunlara neden oldu. As kleopatra yukarıdaki yorumunda yanıt verdi, ben ile sorun çözüldü JFormattedTextField. Bununla birlikte, çözüm biraz daha fazla iş gerektirir, ancak daha temizdir.

JFormattedTextFieldHer metin alanına değiştirdikten sonra varsayılan tetikleyici tarafından bir özellik değişikliği yok. Varsayılan kurucusu JFormattedTextFieldbir biçimlendirici oluşturmaz.

Bununla birlikte, OP'nin önerisini yapmak için commitEdit(), alanın her geçerli düzenlemesinden sonra yöntemi çağıracak bir formatlayıcı kullanmanız gerekir . commitEdit()Yöntem bu odak değişikliği veya basıldığında anahtar girmek varsayılan olarak tetiklenir, Görebildiğim kadarıyla gelen ve biçimlendiricisine olmadan mülkiyet değişikliğini tetikleyen budur.

Daha fazla bilgi için http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value adresine bakın.

Yapıcısı veya ayarlayıcı yöntemi aracılığıyla ya DefaultFormatteraktarılacak varsayılan bir formatter ( ) nesnesi oluşturun JFormattedTextField. Varsayılan biçimlendiricinin bir yöntemi, biçimlendiriciyi metin her değiştiğinde yöntemi setCommitsOnValidEdit(boolean commit)tetikleyecek şekilde ayarlar commitEdit(). Bu daha sonra a PropertyChangeListenerve propertyChange()yöntem kullanılarak alınabilir .


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

Ama sadece kullanıcının (belki de kazada) klavyesinde dokunduğu bir şeyi ayrıştırmak istemiyorum Integer. ExceptionAtılan her şeyi yakalamalı ve JTextFieldboş olmadığından emin olmalısınız .


2

Doküman dinleyici uygulamasını kullanırken runnable yöntemini kullanırsak SwingUtilities.invokeLater () bazen takılıyor ve sonucu güncellemek zaman alıyor (Deneme göre). Bunun yerine, burada belirtildiği gibi metin alanı değişiklik dinleyicisi için KeyReleased olayını da kullanabiliriz .

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

1

Codemwnci'nin güncelleme sürümüdür. kodu oldukça iyi ve hata mesajı dışında harika çalışıyor. Hatadan kaçınmak için koşul ifadesini değiştirmeniz gerekir.

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

Metin alanına length = 0 değerinden daha uzun bir dize girildiğinde uyarlamanız hata mesajı iletişim kutusunu başlatır. Bu temelde boş bir dize dışında herhangi bir dize. İstenen çözüm bu değil.
klaar

0

Kontrol etmek için "MouseExited" bile kullanabilirsiniz. misal:

 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) {

        }

    }   

6
gerçekten değil: metin değiştiğinde gereksinim bir şey yapıyor - bu mouseEvents ;-) ile ilgisiz
kleopatra

0

WindowBuilder'da yepyeni ve aslında birkaç yıl sonra Java'ya geri dönüyorum, ancak "bir şey" uyguladım, sonra bakacağım ve bu iş parçacığına rastlayacağımı düşündüm.

Bunu test etmenin ortasındayım, bu yüzden, tüm bunlara yeni olmaya dayanarak, bir şeyleri kaçırmam gerektiğine eminim.

Burada "runTxt" bir metin kutusu ve "runName" sınıfının bir veri üyesi ne yaptım:

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

Şimdiye kadar burada olanlardan çok daha basit görünüyor ve çalışıyor gibi görünüyor, ancak, bunu yazmanın ortasında olduğum için, gözden kaçan gotcha'ları duymayı takdir ediyorum. Kullanıcının metin kutusuna değişiklik yapmadan girip çıkması bir sorun mu? Bence tek yapman gereken gereksiz bir görev.


-1

ActionListener (girişte tetikleyen) yerine bir KeyListener (herhangi bir tuşta tetikleyen) kullanın


Alanın değeri düzgün şekilde yakalanmadığından bu çalışmaz field.getText(), başlangıç ​​değerini döndürür. ve event ( arg0.getKeyChar()), alan metniyle birleştirilip birleştirilmemeniz gerektiğini belirlemek için basılan hata kontrolünü döndürür.
16'da

@glend, keyTyped olayı yerine keyReleased olayını kullanabilirsiniz. Benim için çalıştı ve tam değeri elde etti.
Kakumanu siva krishna

-1

DocumentFilter ? Size manipüle etme yeteneği verir.

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

Afedersiniz. J Jython (Java'da Python) kullanıyorum - ama anlaşılması kolay

# 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));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.