add the attribute maxNumberCharactersAfterDecimalSeparator
the method get and set to the attribute
and change the method onKeyPress

of course the name of the attribute should be smaller, but to exemplify the best that was found.

Bugs
isn't possible change the value field if the decimal of the value equals maxNumberCharactersAfterDecimalSeparator - now its ok

Example
Code:
MyNumberField field = new MyNumberField();
field.setMaxNumberCharactersAfterDecimalSeparator(4);
Source Code
Code:
package example.client;

import java.util.ArrayList;
import java.util.List;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.event.FieldEvent;
import com.extjs.gxt.ui.client.util.Format;
import com.extjs.gxt.ui.client.widget.form.NumberPropertyEditor;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.form.Validator;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.i18n.client.constants.NumberConstants;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.KeyboardListener;

public class MyNumberField extends TextField<Number> {

    /**
     * NumberField messages.
     */
    public class NumberFieldMessages extends TextFieldMessages {
        private String minText;
        private String maxText;
        private String nanText;
        private String negativeText = GXT.MESSAGES.numberField_negativeText();

        /**
         * Returns the max error text.
         * 
         * @return the error text
         */
        public String getMaxText() {
            return maxText;
        }

        /**
         * Returns the min error text.
         * 
         * @return the min eror text
         */
        public String getMinText() {
            return minText;
        }

        /**
         * Returns the not a number error text.
         * 
         * @return the not a number error text
         */
        public String getNanText() {
            return nanText;
        }

        /**
         * Returns the negative error text.
         * 
         * @return the error text
         */
        public String getNegativeText() {
            return negativeText;
        }

        /**
         * Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}").
         * 
         * @param maxText the max error text
         */
        public void setMaxText(String maxText) {
            this.maxText = maxText;
        }

        /**
         * Sets the Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}").
         * 
         * @param minText min error text
         */
        public void setMinText(String minText) {
            this.minText = minText;
        }

        /**
         * Sets the error text to display if the value is not a valid number. For example, this can happen if a valid character like '.' or '-' is left in the field
         * with no number (defaults to "{value} is not a valid number").
         * 
         * @param nanText the not a number text
         */
        public void setNanText(String nanText) {
            this.nanText = nanText;
        }

        /**
         * Sets the negative error text (defaults to 'The value must be greater or equal to 0').
         * 
         * @param negativeText the error text
         */
        public void setNegativeText(String negativeText) {
            this.negativeText = negativeText;
        }
    }

    private String baseChars = "0123456789";
    private String decimalSeparator = ".";
    private boolean allowNegative = true;
    private boolean allowDecimals = true;
    private List<Character> allowed;
    private Number minValue = Double.NEGATIVE_INFINITY;
    private Number maxValue = Double.MAX_VALUE;
    private int lastKeyCode;
    private final NumberConstants constants = (NumberConstants) GWT.create(NumberConstants.class);

    private Integer maxNumberCharactersAfterDecimalSeparator;

    /**
     * Creates a new number field.
     */
    public MyNumberField() {
        messages = new NumberFieldMessages();
        propertyEditor = new NumberPropertyEditor();
        decimalSeparator = constants.decimalSeparator();
    }

    /**
     * Returns true of decimal values are allowed.
     * 
     * @return the allow decimal state
     */
    public boolean getAllowDecimals() {
        return allowDecimals;
    }

    /**
     * Returns true if negative values are allowed.
     * 
     * @return the allow negative value state
     */
    public boolean getAllowNegative() {
        return allowNegative;
    }

    /**
     * Returns the base characters.
     * 
     * @return the base characters
     */
    public String getBaseChars() {
        return baseChars;
    }

    /**
     * Returns the field's decimal separator.
     * 
     * @return the separator
     */
    public String getDecimalSeparator() {
        return decimalSeparator;
    }

    /**
     * Returns the field's number format.
     * 
     * @return the number format
     */
    public NumberFormat getFormat() {
        return getPropertyEditor().getFormat();
    }

    public Integer getMaxNumberCharactersAfterDecimalSeparator() {
        return maxNumberCharactersAfterDecimalSeparator;
    }

    /**
     * Returns the fields max value.
     * 
     * @return the max value
     */
    public Number getMaxValue() {
        return maxValue;
    }

    @Override
    public NumberFieldMessages getMessages() {
        return (NumberFieldMessages) messages;
    }

    /**
     * Returns the field's minimum value.
     * 
     * @return the min value
     */
    public Number getMinValue() {
        return minValue;
    }

    @Override
    public NumberPropertyEditor getPropertyEditor() {
        return (NumberPropertyEditor) propertyEditor;
    }

    /**
     * Returns the number property editor number type.
     * 
     * @see NumberPropertyEditor#setType(Class)
     * @return the number type
     */
    public Class getPropertyEditorType() {
        return getPropertyEditor().getType();
    }

    @Override
    protected void onKeyDown(FieldEvent fe) {
        super.onKeyDown(fe);
        // must key code in key code as gwt returns character in key press
        lastKeyCode = fe.getKeyCode();
    }

    @Override
    protected void onKeyPress(FieldEvent fe) {
        super.onKeyPress(fe);
        char key = (char) fe.getKeyCode();

        if (fe.isSpecialKey(lastKeyCode) || lastKeyCode == KeyboardListener.KEY_BACKSPACE || lastKeyCode == KeyboardListener.KEY_DELETE || fe.isControlKey()) {
            return;
        }

        if (!allowed.contains(key)) {
            fe.stopEvent();
        }

        if (maxNumberCharactersAfterDecimalSeparator != null && maxNumberCharactersAfterDecimalSeparator.intValue() > 0) {

            StringBuilder sb = new StringBuilder();
            sb.append(String.valueOf(getRawValue()));

            sb.insert(getCursorPos(), String.valueOf((char) fe.getKeyCode()));

            String aux = sb.toString();

            System.out.println(aux);

            if (aux != null && !aux.equals("")) {

                aux = aux.replaceAll("\\,", "\\.");

                String[] vector = aux.split("\\.");

                if (vector.length > 1) {

                    String decimal = vector[1];

                    if (decimal.length() > maxNumberCharactersAfterDecimalSeparator) {
                        fe.stopEvent();
                    }

                }
            }
        }
    }

    @Override
    protected void onRender(Element target, int index) {
        super.onRender(target, index);
        allowed = new ArrayList<Character>();
        for (int i = 0; i < baseChars.length(); i++) {
            allowed.add(baseChars.charAt(i));
        }

        if (allowNegative) {
            allowed.add('-');
        }
        if (allowDecimals) {
            for (int i = 0; i < decimalSeparator.length(); i++) {
                allowed.add(decimalSeparator.charAt(i));
            }
        }
    }

    /**
     * Sets whether decimal value are allowed (defaults to true).
     * 
     * @param allowDecimals true to allow negative values
     */
    public void setAllowDecimals(boolean allowDecimals) {
        this.allowDecimals = allowDecimals;
    }

    /**
     * Sets whether negative value are allowed.
     * 
     * @param allowNegative true to allow negative values
     */
    public void setAllowNegative(boolean allowNegative) {
        this.allowNegative = allowNegative;
    }

    /**
     * Sets the base set of characters to evaluate as valid numbers (defaults to '0123456789').
     * 
     * @param baseChars the base character
     */
    public void setBaseChars(String baseChars) {
        assertPreRender();
        this.baseChars = baseChars;
    }

    /**
     * Sets the character(s) to allow as the decimal separator (defaults to '.').
     * 
     * @param decimalSeparator
     */
    public void setDecimalSeparator(String decimalSeparator) {
        this.decimalSeparator = decimalSeparator;
    }

    /**
     * Sets the cell's number formatter.
     * 
     * @param format the format
     */
    public void setFormat(NumberFormat format) {
        getPropertyEditor().setFormat(format);
    }

    public void setMaxNumberCharactersAfterDecimalSeparator(Integer maxNumberCharactersAfterDecimalSeparator) {
        this.maxNumberCharactersAfterDecimalSeparator = maxNumberCharactersAfterDecimalSeparator;
    }

    /**
     * Sets the field's max allowable value.
     * 
     * @param maxValue the max value
     */
    public void setMaxValue(Number maxValue) {
        this.maxValue = maxValue.doubleValue();
    }

    /**
     * Sets the field's minimum allowed value.
     * 
     * @param minValue the min value
     */
    public void setMinValue(Number minValue) {
        this.minValue = minValue.doubleValue();
    }

    /**
     * Specifies the number type used when converting a String to a Number instance (defaults to Double).
     * 
     * @param type the number type (Short, Integer, Long, Float, Double).
     */
    public void setPropertyEditorType(Class type) {
        getPropertyEditor().setType(type);
    }

    @Override
    protected boolean validateValue(String value) {
        // validator should run after super rules
        Validator tv = validator;
        validator = null;
        if (!super.validateValue(value)) {
            validator = tv;
            return false;
        }
        validator = tv;
        if (value.length() < 1) { // if it's blank and textfield didn't flag it then
            // its valid it's valid
            return true;
        }

        String v = value;

        Number d = null;
        try {
            d = getPropertyEditor().convertStringValue(v);
        } catch (Exception e) {
            System.out.println(e);
            String error = "";
            if (getMessages().getNanText() == null) {
                error = GXT.MESSAGES.numberField_nanText(v);
            } else {
                error = Format.substitute(getMessages().getNanText(), v);
            }
            markInvalid(error);
            return false;
        }
        if (d.doubleValue() < minValue.doubleValue()) {
            String error = "";
            if (getMessages().getMinText() == null) {
                error = GXT.MESSAGES.numberField_minText(minValue.doubleValue());
            } else {
                error = Format.substitute(getMessages().getMinText(), minValue);
            }
            markInvalid(error);
            return false;
        }

        if (d.doubleValue() > maxValue.doubleValue()) {
            String error = "";
            if (getMessages().getMaxText() == null) {
                error = GXT.MESSAGES.numberField_maxText(maxValue.doubleValue());
            } else {
                error = Format.substitute(getMessages().getMaxText(), maxValue);
            }
            markInvalid(error);
            return false;
        }

        if (!allowNegative && d.doubleValue() < 0) {
            markInvalid(getMessages().getNegativeText());
            return false;
        }

        if (validator != null) {
            String msg = validator.validate(this, value);
            if (msg != null) {
                markInvalid(msg);
                return false;
            }
        }

        return true;
    }
}