JFormattedTextField destroys DocumentFilter

余生长醉 提交于 2019-12-10 23:40:12

问题


I have a Problem with the JFormattedTextField (I use it as a base class for all of our text fields).

Today I tried to add a document filter to the document of this field which works just fine, but only as long it doesnt have a formatter factory set.

The Problem is, when the formatter factory is set (in my case the default classes) and processFocusEvent is called following happens (JFormattedTextField.java:595):

    // if there is a composed text, process it first
    if ((ic != null) && composedTextExists) {
    ic.endComposition();
    EventQueue.invokeLater(focusLostHandler);
    } else {
    focusLostHandler.run();
    }
    }
    else if (!isEdited()) {
        // reformat
        setValue(getValue(), true, true);
    }

then setValue() is called (JFormattedTextField.java:757):

private void setValue(Object value, boolean createFormat, boolean firePC) {
    Object oldValue = this.value;

    this.value = value;

    if (createFormat) {
        AbstractFormatterFactory factory = getFormatterFactory();
        AbstractFormatter atf;

        if (factory != null) {
            atf = factory.getFormatter(this);
        }
        else {
            atf = null;
        }
        setFormatter(atf);
    }
    else {
        // Assumed to be valid
        setEditValid(true);
    }

    setEdited(false);

if (firePC) {
    firePropertyChange("value", oldValue, value);
}
}

As you can see if there is a factory it will try to "refresh" the formatter

(JFormattedTextField.java:439):

protected void setFormatter(AbstractFormatter format) {
    AbstractFormatter oldFormat = this.format;

    if (oldFormat != null) {
        oldFormat.uninstall();
    }
    setEditValid(true);
    this.format = format;
    if (format != null) {
        format.install(this);
    }
    setEdited(false);
    firePropertyChange("textFormatter", oldFormat, format);
}

And here is the real Problem I have (JFormattedTextField$AbstractFormatter.class:950):

    public void uninstall() {
        if (this.ftf != null) {
            installDocumentFilter(null);
            this.ftf.setNavigationFilter(null);
            this.ftf.setFormatterActions(null);
        }
    }

Here it destroys my document filter, I know that the formatter holds the documentFilter normally, but was it really intended to work that way? The document should be the object handling its filter (imho) not the formatter. Is there a way to go around it without the use of a specialized formatter subclass?

EXAMPLE CODE: (as requested :) )

package jftf;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;

import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultFormatter;
import javax.swing.text.DocumentFilter;

/**
 * @author Pawel Miler
 */
public class JFormattedTextFieldExample {

private Container container;
private JFormattedTextField workingTextField;
private JFormattedTextField brokenTextField;
private DocumentFilter documentFilter;

public static void main(String[] args) {
    new JFormattedTextFieldExample();
}

public JFormattedTextFieldExample() {
    initializeDocumentFilter();
    initializeTextFields();
    initializeGui();
}

private void initializeDocumentFilter(){
    documentFilter = new UppercaseDocumentFilter();
}

private void initializeTextFields() {
    workingTextField = createTextField(false);
    addDocumentFilter(workingTextField);

    brokenTextField = createTextField(true);
    addDocumentFilter(workingTextField);
}

private JFormattedTextField createTextField(boolean createFormatter) {
    JFormattedTextField textField;
    textField = createFormatter ? new JFormattedTextField(new DefaultFormatter()) : new JFormattedTextField();
    return textField;
}

private void addDocumentFilter(JTextField textField) {
    ((AbstractDocument) textField.getDocument()).setDocumentFilter(documentFilter);
}

private void initializeGui() {
    container = createFrame();

    container.setLayout(new FlowLayout());

    Dimension dimension = new Dimension(80, 20);

    brokenTextField.setPreferredSize(dimension);
    container.add(brokenTextField);

    workingTextField.setPreferredSize(dimension);
    container.add(workingTextField);
}

private Container createFrame() {
    JFrame frame = new JFrame("JFormattedTextField example");
    frame.setSize(200, 70);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);

    return frame.getContentPane();
}

public class UppercaseDocumentFilter extends DocumentFilter {

    public void insertString(FilterBypass filterBypass, int offset, String text, AttributeSet attr) throws BadLocationException {
        super.insertString(filterBypass, offset, text.toUpperCase(), attr);
    }

    public void replace(DocumentFilter.FilterBypass filterBypass, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
        super.replace(filterBypass, offset, length, text.toUpperCase(), attrs);
    }
}
}

Both text fields should have the same document filter, but type in one and it always will get capital letters the other one does not.

Current Solution: (the workaround I wrote moments ago implemented in a subclass of JFormattedTextField, I need tu use the flag in the case the formatter has a documentfilter too, you can't use both at the same time, but I'm not really happy needing one at all)

public boolean isPreserveDocumentFilter() {
    return preserveDocumentFilter;
}

public void setPreserveDocumentFilter(boolean preserveDocumentFilter) {
    this.preserveDocumentFilter = preserveDocumentFilter;
}

/**
 * We need to override if we want to use a documentFilter with DefaultFormatter implementation.
 * For more info see: <a href="http://stackoverflow.com/questions/20074778/jformattedtextfield-destroys-documentfilter">info</a>
 */
@Override
protected void setFormatter(AbstractFormatter format) {
    Document doc = this.getDocument();
    DocumentFilter filter = null;

    if (preserveDocumentFilter) {
        if ( doc instanceof AbstractDocument ) {
            filter = ((AbstractDocument) doc).getDocumentFilter();
        }
    }

    super.setFormatter(format);

    if ( filter != null ) {
        ((AbstractDocument) doc).setDocumentFilter(filter);
    }
}

回答1:


I faced the same problem. Basically, the right way seems to be: override getDocumentFilter() on the AbstractFormatter itself

protected DocumentFilter getDocumentFilter()

Subclass and override if you wish to provide a DocumentFilter to restrict what can be input. install will install the returned value onto the JFormattedTextField.

from https://docs.oracle.com/javase/7/docs/api/javax/swing/JFormattedTextField.AbstractFormatter.html



来源:https://stackoverflow.com/questions/20074778/jformattedtextfield-destroys-documentfilter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!