0

I want to format a number as the user enters the number in an edit field.

I am using below code to format the number as the user changes focus to another control, using the onfocus() function:

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

This doesn't solve the problem of formatting the numbers as the user types, though.

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
allDroid
  • 405
  • 1
  • 10
  • 21
  • Is the problem because the function doesn't return the required value, or because it isn't called on focus? or is it called properly but still the field value doesn't change? Also, what type of widget do you use? – Arye Shemesh Aug 12 '15 at 05:59
  • @Arye Shemesh i want the number to get formatted as user enter's number simultaniously in editfield.Now i am using onfocus so that if i have two editfields on focus to second editfield number in first editfied get formatted,but i dont want my app to work like that,so i want to make number formatted as user enter's number in editfield. – allDroid Aug 12 '15 at 09:06
  • Sounds like you should add it as a modify listener. Focus happens only once before the user starts typing. – Arye Shemesh Aug 12 '15 at 09:57
  • @Arye Shemesh if i have to use modify listener like fieldchangelistener ,how could i modify my code so that number gets formatted as i enter in field.could u show me with example please.if i enter 1234322 it should get displayed in 1,234,322.00 format when field starts to change. – allDroid Aug 12 '15 at 17:02
  • Assuming you're using swt Text widget: Text text = new Text(parent, SWT.NONE); text.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { Text t = (Text) e.widget; String oldText = t.getText(); String newText = "xxx" + oldText; t.setText(newText); } }); – Arye Shemesh Aug 13 '15 at 05:46
  • @Arye Shemesh in Javame we dont have controls like swt text widget – allDroid Aug 13 '15 at 16:52

1 Answers1

0

The obvious solution to me is to override the paint method of the EditField so that its displayed as formatted text. The field's getText would return "12345" while it would display as "12 345" for example.

This is almost perfect for what you need. But you need to remember to handle the cursor drawing as well. You don't want the native cursor on the 5 but drawing on the 3.

EDIT

This should help you out. The important parts here are paint and getFocusRect. Please note that this is pretty rough still. It doesn't handle touch events (cursor won't move correctly), and there will be no line wrapping (must be handled by layout and paint methods).

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();
    }
}
Kevin
  • 1,626
  • 16
  • 30
  • Could u show me with example please.if i enter number ex:2345456 it should get displayed as 2,345,456.00 there should be a decimel point and two zeros after that decimal point.these formatting things should happen as user types in editfield.Cant i use Fieldchangelistener ?if so how? – allDroid Aug 12 '15 at 16:53
  • Added an example implementation, will still need some work by you though – Kevin Aug 13 '15 at 08:38
  • this works fine ,but after 17 digits its get roundedup as it is in code i tried modifying and removing codeor function which does rounding of number but when i do this i am not able to type anything in textfield,where i am going wrong? – allDroid Aug 23 '15 at 14:23
  • This is because of `Formatter f = new Formatter("en");`. As far as I know the formatter only supports numbers of a certain length. If you want more, you'll need to use something other than the formatter. – Kevin Aug 23 '15 at 16:56
  • thanks,This was very useful for me.Here i have given user to enter only 17 numbers as it gets rounded up on next digit.My doubth was that cant i make any changes in functions where it checks for last digit and set condition of roundtxt to true or false? – allDroid Aug 24 '15 at 02:45
  • I suppose it should be possible to round the number up or down before it gets given to the formatter. As the first step of `formatNumber` – Kevin Aug 26 '15 at 06:10