如何使用 fieldchangelistener 格式化数字

How to format number using fieldchangelistener

我想在用户在编辑字段中输入数字时设置数字格式。

我使用以下代码在用户将焦点切换到另一个控件时使用 onfocus() 函数来格式化数字:

    public static String formatNumber(double number, int decimals, String digitGrouping){
            Formatter f = new Formatter("en");
            String rawNumber = f.formatNumber(number, decimals+1);

            String rawIntString = rawNumber.substring(0, rawNumber.indexOf(".")); //Basically intString without digit grouping
            StringBuffer intString = new StringBuffer();
            StringBuffer decString = new StringBuffer(rawNumber.substring(rawNumber.indexOf(".")+1));
            StringBuffer formattedNumber = new StringBuffer();
            int workingVal = 0;
            int newNum = 0;
            boolean roundNext;

            //Add digit grouping
            int grouplen = 0;
            int firstDigit;
            if(rawIntString.charAt(0) == '-'){
                firstDigit = 1;
            }else{
                firstDigit = 0;
            }
            for(int n=rawIntString.length()-1;n>=firstDigit;n--){
                intString.insert(0, rawIntString.substring(n, n+1));
                grouplen++;
                if(grouplen == 3 && n>firstDigit){
                    intString.insert(0, digitGrouping);
                    grouplen = 0;
                }
            }

            //First, check the last digit
            workingVal = Integer.parseInt(String.valueOf(decString.charAt(decString.length()-1)));
            if(workingVal>=5){
                roundNext = true;
            }else{
                roundNext = false;
            }
            //Get the decimal values, round if needed, and add to formatted string buffer
            for(int n=decString.length()-2;n>=0;n--){
                workingVal = Integer.parseInt(String.valueOf(decString.charAt(n)));
                if(roundNext == true){
                    newNum = workingVal + 1;
                    if(newNum == 10){
                        roundNext = true;
                        newNum = 0;
                    }else{
                        roundNext = false;
                    }
                    formattedNumber.insert(0, newNum);
                }else{
                    formattedNumber.insert(0, workingVal);
                }
            }
            //Now get the integer values, round if needed, and add to formatted string buffer
            formattedNumber.insert(0, ".");
            for(int n=intString.length()-1;n>=0;n--){
                try{
                    workingVal = Integer.parseInt(String.valueOf(intString.charAt(n)));
                }catch(Exception e){
                    formattedNumber.insert(0, intString.charAt(n));
                    continue;
                }
                if(roundNext == true){
                    newNum = workingVal + 1;
                    if(newNum == 10){
                        roundNext = true;
                        newNum = 0;
                    }else{
                        roundNext = false;
                    }
                    formattedNumber.insert(0, newNum);
                }else{
                    formattedNumber.insert(0, workingVal);
                }   
            }

            //Just in case its a number like 9999.99999 (if it rounds right to the end
            if(roundNext == true){
                formattedNumber.insert(0, 1);

            }   

            //re-add the minus sign if needed
            if(firstDigit == 1) formattedNumber.insert(0, rawIntString.charAt(0));

            if(digitGrouping.length() > 0){
                if(formattedNumber.toString().indexOf(".") == -1){
                    //no decimal
                    if(formattedNumber.toString().indexOf(digitGrouping) > 3+firstDigit){
                        formattedNumber.insert(1+firstDigit, digitGrouping);
                    }

                    if(formattedNumber.toString().length() == 4+firstDigit){
                        formattedNumber.insert(1+firstDigit, digitGrouping);
                    }
                }else{
                    //no decimal
                    if(formattedNumber.toString().indexOf(digitGrouping) > 3+firstDigit){
                        formattedNumber.insert(1+firstDigit, digitGrouping);
                    }

                    String intportion = formattedNumber.toString().substring(0, formattedNumber.toString().indexOf("."));
                    if(intportion.length() == 4+firstDigit){
                        formattedNumber.insert(1+firstDigit, digitGrouping);
                    }
                }
            }

            //now remove trailing zeros
            String tmp = formattedNumber.toString();
            int newLength = tmp.length();
            for(int n=tmp.length()-1;n>=0;n--){
                if(tmp.substring(n, n+1).equalsIgnoreCase("0")){
                    newLength--;
                }else{
                    if(tmp.substring(n, n+1).equalsIgnoreCase(".")) newLength--;
                    break;
                }
            }
            formattedNumber.setLength(newLength);

            return formattedNumber.toString();
        }

不过,这并没有解决用户键入时格式化数字的问题。

对我来说显而易见的解决方案是重写 EditField 的绘制方法,使其显示为格式化文本。该字段的 getText 将 return "12345" 而它会显示为 "12 345" 例如。

几乎完全满足您的需求。但是您还需要记住处理光标绘制。您不想在 5 上使用本机光标,而是在 3 上绘图。

编辑

这应该能帮到你。这里的重要部分是 paintgetFocusRect。请注意,这仍然很粗糙。它不处理触摸事件(光标不会正确移动),也不会换行(必须由 layoutpaint 方法处理)。

public class FormattedEditField extends TextField
{
    public FormattedEditField()
    {
        setFilter(TextFilter.get(TextFilter.NUMERIC));
    }

    protected String getFormattedText()
    {
        String text = getText();
        if ((text == null) || (text.length() == 0))
        {
            return "";
        }
        else
        {
            double nr = Double.parseDouble(text);
            return formatNumber(nr, 2, ",");
        }
    }

    public synchronized void getFocusRect(XYRect rect)
    {
        // Tell Blackberry where to draw the cursor
        Font font = getFont();
        String formattedText = getFormattedText();
        int length = formattedText.length();

        if (length == 0)
        {
            rect.x = getTextOriginX();
            rect.width = font.getAdvance('1');
            rect.y = getTextOriginY();
            rect.height = font.getHeight();
        }
        else
        {
            int cursorPosition = getCursorPosition();

            int i;
            for (i = 0; i <= cursorPosition; i++)
            {
                if (cursorPosition == length)
                {
                    break;
                }

                if (formattedText.charAt(i) == ',')
                {
                    cursorPosition++;
                }
            }

            if (cursorPosition >= length)
            {
                rect.x = getTextOriginX() + font.getAdvance(formattedText);
                rect.width = font.getAdvance('1');
            }
            else
            {
                rect.x = getTextOriginX() + font.getAdvance(formattedText.substring(0, cursorPosition));
                rect.width = font.getAdvance(formattedText.charAt(cursorPosition));
            }

            rect.y = getTextOriginY();
            rect.height = font.getHeight();
        }
    }

    protected void drawFocus(Graphics graphics, boolean on)
    {
    }

    protected synchronized void paint(Graphics graphics)
    {
        int x = getTextOriginX();
        int y = getTextOriginY();

        String text = getFormattedText();

        if (isFocus())
        {
            XYRect rect = new XYRect();
            getFocusRect(rect);

            int colour = graphics.getColor();
            graphics.setColor(0x207CFE);
            graphics.fillRect(rect.x, rect.y, rect.width, rect.height);
            graphics.setColor(colour);

            graphics.drawText(text, x, y);
            graphics.invert(rect);
        }
        else
        {
            graphics.drawText(text, x, y);
        }
    }

    // Since we are overriding the painting, we should accommodate the different drawing flags
    private int getTextOriginX()
    {
        int x = 0;

        if ((getStyle() & DrawStyle.HCENTER) == DrawStyle.HCENTER)
        {
            x = (getWidth() - getFont().getAdvance(getFormattedText())) / 2;
        }
        else if ((getStyle() & DrawStyle.RIGHT) == DrawStyle.RIGHT)
        {
            x = getWidth() - getFont().getAdvance(getFormattedText());
        }

        return x;
    }

    // Since we are overriding the painting, we should accommodate the different drawing flags
    private int getTextOriginY()
    {
        int y = 0;
        if ((getStyle() & DrawStyle.VCENTER) == DrawStyle.VCENTER)
        {
            y = (getHeight() - getFont().getHeight()) / 2;
        }
        else if ((getStyle() & DrawStyle.BOTTOM) == DrawStyle.BOTTOM)
        {
            y = getHeight() - getFont().getHeight();
        }

        return y;
    }

    // This method is from http://supportforums.blackberry.com/t5/Java-Development/Format-a-decimal-number/m-p/763981#M142257
    // I don't know how good it is, since I use my own method for this
    private String formatNumber(double number, int decimals, String digitGrouping)
    {
        Formatter f = new Formatter("en");
        String rawNumber = f.formatNumber(number, decimals + 1);

        String rawIntString = rawNumber.substring(0, rawNumber.indexOf(".")); // Basically intString without digit grouping
        StringBuffer intString = new StringBuffer();
        StringBuffer decString = new StringBuffer(rawNumber.substring(rawNumber.indexOf(".") + 1));
        StringBuffer formattedNumber = new StringBuffer();
        int workingVal = 0;
        int newNum = 0;
        boolean roundNext;

        // Add digit grouping
        int grouplen = 0;
        int firstDigit;
        if (rawIntString.charAt(0) == '-')
        {
            firstDigit = 1;
        }
        else
        {
            firstDigit = 0;
        }
        for (int n = rawIntString.length() - 1; n >= firstDigit; n--)
        {
            intString.insert(0, rawIntString.substring(n, n + 1));
            grouplen++;
            if (grouplen == 3 && n > firstDigit)
            {
                intString.insert(0, digitGrouping);
                grouplen = 0;
            }
        }

        // First, check the last digit
        workingVal = Integer.parseInt(String.valueOf(decString.charAt(decString.length() - 1)));
        if (workingVal >= 5)
        {
            roundNext = true;
        }
        else
        {
            roundNext = false;
        }
        // Get the decimal values, round if needed, and add to formatted string buffer
        for (int n = decString.length() - 2; n >= 0; n--)
        {
            workingVal = Integer.parseInt(String.valueOf(decString.charAt(n)));
            if (roundNext == true)
            {
                newNum = workingVal + 1;
                if (newNum == 10)
                {
                    roundNext = true;
                    newNum = 0;
                }
                else
                {
                    roundNext = false;
                }
                formattedNumber.insert(0, newNum);
            }
            else
            {
                formattedNumber.insert(0, workingVal);
            }
        }
        // Now get the integer values, round if needed, and add to formatted string buffer
        formattedNumber.insert(0, ".");
        for (int n = intString.length() - 1; n >= 0; n--)
        {
            try
            {
                workingVal = Integer.parseInt(String.valueOf(intString.charAt(n)));
            }
            catch (Exception e)
            {
                formattedNumber.insert(0, intString.charAt(n));
                continue;
            }
            if (roundNext == true)
            {
                newNum = workingVal + 1;
                if (newNum == 10)
                {
                    roundNext = true;
                    newNum = 0;
                }
                else
                {
                    roundNext = false;
                }
                formattedNumber.insert(0, newNum);
            }
            else
            {
                formattedNumber.insert(0, workingVal);
            }
        }

        // Just in case its a number like 9999.99999 (if it rounds right to the end
        if (roundNext == true)
        {
            formattedNumber.insert(0, 1);
        }

        // re-add the minus sign if needed
        if (firstDigit == 1)
            formattedNumber.insert(0, rawIntString.charAt(0));

        if (digitGrouping.length() > 0)
        {
            if (formattedNumber.toString().indexOf(".") == -1)
            {
                // no decimal
                if (formattedNumber.toString().indexOf(digitGrouping) > 3 + firstDigit)
                {
                    formattedNumber.insert(1 + firstDigit, digitGrouping);
                }

                if (formattedNumber.toString().length() == 4 + firstDigit)
                {
                    formattedNumber.insert(1 + firstDigit, digitGrouping);
                }
            }
            else
            {
                // no decimal
                if (formattedNumber.toString().indexOf(digitGrouping) > 3 + firstDigit)
                {
                    formattedNumber.insert(1 + firstDigit, digitGrouping);
                }

                String intportion = formattedNumber.toString().substring(0, formattedNumber.toString().indexOf("."));
                if (intportion.length() == 4 + firstDigit)
                {
                    formattedNumber.insert(1 + firstDigit, digitGrouping);
                }
            }
        }

        // now remove trailing zeros
        String tmp = formattedNumber.toString();
        int newLength = tmp.length();
        for (int n = tmp.length() - 1; n >= 0; n--)
        {
            if (tmp.substring(n, n + 1).equalsIgnoreCase("0"))
            {
                newLength--;
            }
            else
            {
                if (tmp.substring(n, n + 1).equalsIgnoreCase("."))
                    newLength--;
                break;
            }
        }
        formattedNumber.setLength(newLength);

        return formattedNumber.toString();
    }
}