1. java
  2. android
  3. c#
  4. .net
  5. javascript
  6. php
  7. jquery
  8. html
  9. sql

JavaFX - TextField com máscara que aceita somente valor monetário

Estou tentando fazer um TextField do Java FX com uma máscara monetária, utilizando como base uma classe que peguei aqui no fórum, mas que era u JTextField do swing.

A classe:

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.TextField;

public class MonetaryTextField extends TextField {

    private static final NumberFormat MONETARY_FORMAT = new DecimalFormat("R$ #,##0.00");
    private NumberFormat numberFormat;
    private int limit = -1;

    public MonetaryTextField(int _positionsDecimal) {
        this(new DecimalFormat((_positionsDecimal == 0 ? "#,##0" : "#,##0.") + makeZeros(_positionsDecimal)));
    }

    public MonetaryTextField() {
        this(MONETARY_FORMAT);
    }

    public MonetaryTextField(final NumberFormat numberFormat) {
        this.numberFormat = numberFormat;
        setAlignment(Pos.CENTER_RIGHT);

        final StringProperty text = new SimpleStringProperty(this.getText());
        text.addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) {
                setText(newValue);
            }
        });

        this.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                String text = new StringBuilder(MonetaryTextField.this.getText().replaceAll("[^0-9]", "")).toString();
                if (text.isEmpty()) {
                    text = "0";
                } else {
                    text = new BigInteger(text).toString();
                }
                String s = numberFormat.format(new BigDecimal(getLimit() > 0 == text.length() > getLimit()
                        ? text.substring(0, getLimit())
                        : text).divide(new BigDecimal(Math.pow(10, numberFormat.getMaximumFractionDigits()))));

//--------------AQUI O PROBLEMA-----------------------------
            }
        });

        /*Sempre posiciona o cursor ao final do campo*/
        this.caretPositionProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                positionCaret(getText().length());
            }
        });
        setText("0");
        positionCaret(getText().length());
    }

    public final BigDecimal getValue() {
        return new BigDecimal(getText().replaceAll("[^0-9]", "")).divide(new BigDecimal(Math.pow(10, numberFormat.getMaximumFractionDigits())));
    }

    public void setValue(BigDecimal value) {
        super.setText(numberFormat.format(value));
    }

    public NumberFormat getNumberFormat() {
        return numberFormat;
    }

    public void setNumberFormat(NumberFormat numberFormat) {
        this.numberFormat = numberFormat;
    }

    private static final String makeZeros(int zeros) {
        if (zeros >= 0) {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < zeros; i++) {
                builder.append('0');
            }
            return builder.toString();
        } else {
            throw new RuntimeException("Número de casas decimais inválida (" + zeros + ")");
        }
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }
}

Procure no código onde coloquei um comentário dizendo onde está o problema.

O problema é: Quando chamo o método setText(s) naquele local, o método changed(ObservableValue<? extends String> observable, String oldValue, String newValue) é invocado novamente estourando uma StackOverFlow, óbvio.

Então criei uma StringProperty para que ela tomasse conta do que fosse inserido no campo, mas apareceu outro problema que está tirando minhas noites de sono. Quando insiro os números ocorre tudo normalmente, mas quando começo a apagar estoura uma exception:

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: The start must be <= the end

Masssss... o comportamento do valor que foi inserido é perfeito, único problema é que estoura essa exception.

Já fiz cada gambiarra que nem vale a pena mostrar.. enfim, quem puder colaborar e tiver algo a acrescentar, obrigadaço.

  1. Você vai ver essas setas em qualquer página de pergunta. Com elas, você pode dizer se uma pergunta ou uma resposta foram relevantes ou não.
  2. Edite sua pergunta ou resposta caso queira alterar ou adicionar detalhes.
  3. Caso haja alguma dúvida sobre a pergunta, adicione um comentário. O espaço de respostas deve ser utilizado apenas para responder a pergunta.
  4. Se o autor da pergunta marcar uma resposta como solucionada, esta marca aparecerá.
  5. Clique aqui para mais detalhes sobre o funcionamento do GUJ!

0 resposta

Não é a resposta que estava procurando? Procure outras perguntas com as tags javafx ou faça a sua própria pergunta.