kotlincalculatorseparatordecimalformatdigit-separator

How to add thousands separator to numbers in realtime for calculator app. KOTLIN


I have a calculator app and I want it to show large numbers with spaces between them when users enter their input in real time and when the calculator gives a number greater than 999. Currently, they don't have spaces between them like this: 10000; 100000; 1000000. I want them to show like this: 10 000; 100 000; 1 000 000. The second one looks much neater.

Here is my code

 class CalculatorActivity : AppCompatActivity() {

@Suppress("PrivatePropertyName")
private var DigitOnScreen = StringBuilder(12)
private var operation: Char = ' '
private var leftHandSide: Double = 0.0
private var rightHandSide: Double = 0.0
private lateinit var binding: ActivityCalculatorBinding


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding = ActivityCalculatorBinding.inflate(layoutInflater)
    setContentView(binding.root)

    binding.resultId.text = "0"

    initializeButtons()
}

private fun initializeButtons() {
    functionalButtons()
    operationalButtons()
    numericalButtons()
}

/**
 * This function initializes all of our numerical buttons from
 *  [0 - 9]
 */
private fun numericalButtons() {

    binding.oneBtn.setOnClickListener {
        appendToDigitOnScreen("1")
    }

    binding.twoBtn.setOnClickListener {
        appendToDigitOnScreen("2")
    }

    binding.threeBtn.setOnClickListener {
        appendToDigitOnScreen("3")
    }

    binding.fourBtn.setOnClickListener {
        appendToDigitOnScreen("4")
    }

    binding.fiveBtn.setOnClickListener {
        appendToDigitOnScreen("5")
    }

    binding.sixBtn.setOnClickListener {
        appendToDigitOnScreen("6")
    }

    binding.sevenBtn.setOnClickListener {
        appendToDigitOnScreen("7")
    }

    binding.eightBtn.setOnClickListener {
        appendToDigitOnScreen("8")
    }

    binding.nineBtn.setOnClickListener {
        appendToDigitOnScreen("9")
    }

    binding.zeroBtn.setOnClickListener {
        appendToDigitOnScreen("0")
    }

    binding.dotBtn.setOnClickListener {
        appendToDigitOnScreen(".")
    }

}

/**
 *  Insert the button been clicked onto the screen so user can see
 *  inputs for the button clicked
 */

private fun appendToDigitOnScreen(digit: String) {

    // Add each digit to our string builder
    DigitOnScreen.append(digit)

    // display it on the screen of our mobile app
    binding.resultId.text = DigitOnScreen.toString()
}

/**
 *  Initialize the operation keys in our calculator like the
 *  addition key, subtraction key and the likes
 */
private fun operationalButtons() {

    binding.additionBtn.setOnClickListener {
        selectOperation('A')
    }

    binding.subtractBtn.setOnClickListener {
        selectOperation('S')
    }

    binding.divideBtn.setOnClickListener {
        selectOperation('D')
    }

    binding.multipyBtn.setOnClickListener {
        selectOperation('M')
    }

}

/**
 * Function to assign operational sign to our math calculations
 */
private fun selectOperation(c: Char) {

    operation = c
    leftHandSide = DigitOnScreen.toString().toDouble()
    DigitOnScreen.clear()
    binding.resultId.text = "0"
}

/**
 * Handles functional operations in out application like
 * clear button, backspace button and the clear everything button
 */
private fun functionalButtons() {

    binding.clearEverythingBtn.setOnClickListener {
        DigitOnScreen.clear()
        binding.resultId.text = "0"
    }

    binding.clearBtn.setOnClickListener {

        if (DigitOnScreen.isEmpty()) {
            return@setOnClickListener
        } else {
            clearDigit()
        }
    }

    binding.backspaceBtn.setOnClickListener {
        if (DigitOnScreen.isEmpty()) {
            return@setOnClickListener
        } else {
            clearDigit()
        }
    }

    binding.equalBtn.setOnClickListener {
        performMathOperation()
    }

}


/**
 *  This function performs our Math Operation which is then showed on the screen.
 */
private fun performMathOperation() {

    rightHandSide = DigitOnScreen.toString().toDouble()


    when (operation) {

        'A' -> {
            val sum = OperationsHelper.add(leftHandSide, rightHandSide)
            binding.resultId.text = sum.toString()
            DigitOnScreen.clear()
            DigitOnScreen.append(sum)
        }
        'S' -> {
            val subtract = OperationsHelper.subtract(leftHandSide, rightHandSide)
            binding.resultId.text = subtract.toString()
            DigitOnScreen.clear()
            DigitOnScreen.append(subtract)
        }
        'M' -> {
            val multiply = OperationsHelper.multiply(leftHandSide, rightHandSide)
            binding.resultId.text = multiply.toString()
            DigitOnScreen.clear()
            DigitOnScreen.append(multiply)
        }
        'D' -> {
            val divide = OperationsHelper.divide(leftHandSide, rightHandSide)
            binding.resultId.text = divide.toString()
            DigitOnScreen.clear()
            DigitOnScreen.append(divide)
        }

    }

}

/**
 *  This function remove the last digit on the screen.
 */
private fun clearDigit() {

    val length = DigitOnScreen.length

    DigitOnScreen.deleteCharAt(length - 1)
    if (length <= 0) {
        binding.resultId.text = "0"
    }else{
        binding.resultId.text = DigitOnScreen.toString()
    }

}

I need an answer specifically for textView NOT editText


Solution

  • You can include locale-specific group separators with the formatting specifier ,. I would first try to write it this way:

    binding.resultId.text = String.format("%,f", DigitOnScreen.toString().toDouble())
    

    Then to get the right number of decimals, i.e. corresponding to the user input, you have to count the digits after the decimal separator. Here you explicitly use . as the decimal separator for DigitOnScreen.

    val numberAsString = DigitOnScreen.toString()
    val nofDecimals = numberAsString.substringAfter('.', "").length
    binding.resultId.text = String.format("%,.${nofDecimals}f", numberAsString.toDouble())
    

    Note also that in your case, DigitOnScreen is not the number as seen on the screen, and that on screen (what is in binding.resultId.text) the group and decimal separators will depend on the current app's locale.