Android Question EditText with formatted numbers

RB Smissaert

Well-Known Member
Licensed User
Is it somehow possible to have an EditText, holding decimal numbers, where the number shows as x decimals, but where the actual value (the value you get if you do edtTest.Text) remains at y decimals
as when the value was set for that EditText? So, for example I do edtTest.Text = 12.1234 then the actual value should remain 12.1234, but would show as 12.1.
I can do this with parallel variables, so the variables hold the actual values I want, but the textboxes hold the altered values (less decimals), but this adds quite a bit of code and it would be very useful if there was a textbox that has a property show y decimals, but hold the actual value that was put in with x decimals.

RBS
 

emexes

Well-Known Member
Licensed User
So, for example I do edtTest.Text = 12.1234 then the actual value should remain 12.1234, but would show as 12.1
What happens in this example when, say, the cursor is at the end of the visible string and the user presses eg "3"? Is the behind-the-scenes-true-value now 12.3, 12.13, 12.1233 or or 12.12343? How will the user know that it is now eg 12.13 when it still shows as 12.1 (since is displayed to 1 decimal place).

More generally, how can the user edit the part of the number that they cannot see?

If you could zoom out a bit and describe what/why you need this, that might help prompt a solution.

I had a similar problem where say speed was stored internally as km/h but could be presented in a settings screen as km/h, mph or knots, and I had to keep a parallel copy of the values so that they did not get mangled by the conversion process when saving settings from screen back to file. Eg 65 km/h displays as 40 mph (rounded from 40.389) but that would convert back to 64 km/h (rounded from 64.374).
 

emexes

Well-Known Member
Licensed User
Perhaps store the true/unrounded figure in the EditText's Tag property, and then copy it to/from the .Text property with the GotFocus and LostFocus events (or whatever they're called) (assuming they exist).
 

RB Smissaert

Well-Known Member
Licensed User
What happens in this example when, say, the cursor is at the end of the visible string and the user presses eg "3"? Is the behind-the-scenes-true-value now 12.3, 12.13, 12.1233 or or 12.12343? How will the user know that it is now eg 12.13 when it still shows as 12.1 (since is displayed to 1 decimal place).

More generally, how can the user edit the part of the number that they cannot see?

If you could zoom out a bit and describe what/why you need this, that might help prompt a solution.

I had a similar problem where say speed was stored internally as km/h but could be presented in a settings screen as km/h, mph or knots, and I had to keep a parallel copy of the values so that they did not get mangled by the conversion process when saving settings from screen back to file. Eg 65 km/h displays as 40 mph (rounded from 40.389) but that would convert back to 64 km/h (rounded from 64.374).
Yes, I forgot to say that if the user is in the edittext and typing in data the edittext behaves normal, so actual value = displayed value. It is only when the edittext is altered in code that the formatting comes into play. This is how I have code now. My particular case has to do with switching between and imperial and metric height and weight.

RBS
 

emexes

Well-Known Member
Licensed User
To do it your way, this seems to mostly work, although:

1/ you're right, it is a bit codey, and
2a/ you'll have to fill in both the .Text and the .Tag fields when loading up the screen, and
2b/ get the current values from the .Tag fields when saving the screen to file or whatever, and
3/ I don't want to think what happens for IsNumber = False

B4X:
Sub EditText3_FocusChanged (HasFocus As Boolean)
   
    If HasFocus Then
        EditText3.Text = EditText3.Tag
    Else
        EditText3.Tag = EditText3.Text
        If IsNumber(EditText3.Tag) Then
            EditText3.Text = NumberFormat2(EditText3.Tag, 1, 1, 1, False)
        End If
    End If
   
End Sub
 

emexes

Well-Known Member
Licensed User
The way that I did it last time, for a mass that is stored internally as kg, but displayable/editable in either kg or lb, was:

Loading up the settings screen (trimmed down to just the EditText txtTowedMass field):
B4X:
Sub Activity_Resume

    ...

    Dim Temp As String = Shared.Settings.Get(Shared.CurrToweeN & "Mass")
    If IsNumber(Temp) Then
        Temp = NumberFormat2(Shared.ConvertToForceUnit(Temp), 1, 1, 0, False)
    End If
    txtTowedMass.Text = Temp
  
    OldTowedMass = txtTowedMass.Text.Trim    'save to check for change to field

    ....

End Sub
Saving the settings screen:
B4X:
Sub btnSave_Click
  
    Shared.Settings.Put(Shared.CurrToweeN & "Serial", txtSerial.Text.Trim.ToUpperCase)
    Shared.Settings.Put(Shared.CurrToweeN & "Make", txtMake.Text.Trim)
    Shared.Settings.Put(Shared.CurrToweeN & "Description", txtDescription.Text.Trim)
  
    Dim NewTowedMass As String = txtTowedMass.Text.Trim
  
    If NewTowedMass.CompareTo(OldTowedMass) = 0 Then
        'no change, no need to update
    Else
        Dim Temp As String = Shared.NumberQuery(NewTowedMass)
        If IsNumber(Temp) Then
            Dim Temp As String = NumberFormat2(Shared.ConvertFromForceUnit(Abs(Temp)), 1, 3, 0, False)
        End If
        Shared.Settings.Put(Shared.CurrToweeN & "Mass", Temp)
    End If
  
    Shared.WriteSettings
  
    StartActivity(Main)
  
End Sub
 

RB Smissaert

Well-Known Member
Licensed User
This is how I coded and works all fine, but may do some code refactoring to make this neater.

B4X:
Sub Globals()
 
 'for holding the actual real values, not the displayed values
 Private bCopyEditText As Boolean
 Private dHeightCm As Double
 Private iHeightFeet As Int
 Private dHeightInches As Double
 Private dWeightKg As Double
 Private iWeightStones As Int
 Private dWeightPounds As Double
 Private dBMI As Double
 
 Private bMetric As Boolean
 Private dFeet2Cm As Double = 30.48
 Private dInch2Cm As Double = 2.54
 Private dStone2Kg As Double = 6.35029
 Private dPound2Kg As Double = 0.453592


 Type tCalculator(BMI As Int, _
      EDD As Int)
 Private eCalculatorType As tCalculator
 
 Type tImperialWeight(Stones As Int, _
       Pounds As Double)
       
 Type tImperialHeight(Feet As Int, _
       Inches As Double)
 
 eCalculatorType.BMI = 1
 eCalculatorType.EDD = 2
 Private iLastCalculator As Int

End Sub


Sub CInt(o As Object) As Int
 Return Floor(o)
End Sub

Sub CDbl(o As Object) As Double
 Return o
End Sub

Sub ShowBMICalculator
 
 Dim bImperial As Boolean
 
 If iLastCalculator = eCalculatorType.BMI Then
  GotoPanel(ePanelType.Calculators,False,False)
  lblACToolbarTitle.Text = "BMI"
  lblPatCalculator.Text = GetPatLabelString(-1)
  Return
 End If
 
 bCopyEditText = True '<<<<
 
 If pnlCalculators.IsInitialized Then
  pnlCalculators.RemoveAllViews
 End If
 
 If pnlCalculators.IsInitialized = False Then
  Activity.LoadLayout("Calculators")
  arrPanels(ePanelType.Calculators) = pnlCalculators
 End If
 
 pnlCalculators.LoadLayout("BMI")
 
 GotoPanel(ePanelType.Calculators,False,False)
 lblACToolbarTitle.Text = "BMI"
 lblPatCalculator.Text = GetPatLabelString(-1)
 
 General.RunLog("ShowBMICalculator, bMetric: " & bMetric)
 
 bImperial = bMetric = False
 
 edtHeight2.Visible = bImperial
 edtWeight2.Visible = bImperial
 lblInch.Visible = bImperial
 lblPounds.Visible = bImperial
 
 chkMetric.Checked = bMetric
 
 If bMetric Then
  lblCm.Text = "Cm"
  lblKg.Text = "Kg"
 Else
  lblCm.Text = "Feet"
  lblKg.Text = "Inches"
 End If
 
 iLastCalculator = eCalculatorType.BMI
 
End Sub

Sub MetricHeight2Imperial(iCm As Int) As tImperialHeight

 Dim tIH As tImperialHeight
 
 General.RunLog("MetricHeight2Imperial, Round2(1.6, 0): " & Round2(1.6, 0))

 tIH.Feet = CInt(iCm / dFeet2Cm)
 tIH.Inches = (iCm - tIH.Feet * dFeet2Cm) / dInch2Cm
 
 Return tIH

End Sub

Sub MetricWeight2Imperial(dKg As Double) As tImperialWeight

 Dim tIW As tImperialWeight
 
 tIW.Stones = CInt(dKg / dStone2Kg)
 tIW.Pounds = (dKg - tIW.Stones * dStone2Kg) / dPound2Kg
    
 Return tIW

End Sub

Sub ImperialWeight2Kg(iStones As Int, dPounds As Double) As Double
 Return iStones * dStone2Kg + dPounds * dPound2Kg
End Sub

Sub ImperialHeight2Cm(iFeet As Int, dInches As Double) As Double
 Return iFeet * dFeet2Cm + dInches * dInch2Cm
End Sub

Sub btnCalculateBMI_Click
 
 If edtWeight.Text.Length = 0 Then
  Return
 End If
 
 If edtHeight.Text.Length = 0 Then
  Return
 End If
 
 If bMetric Then
  dBMI = dWeightKg / Power(dHeightCm / 100, 2)
 Else
  dBMI = ImperialWeight2Kg(iWeightStones, dWeightPounds) / Power(ImperialHeight2Cm(iHeightFeet, dHeightInches) / 100, 2)
 End If
 
 bCopyEditText = False
 edtBMI.Text = Round2(dBMI, 1)
 
End Sub

Sub btnCalculateForBMI_Click 'calculate the weight to get BMI of x
 
 Dim tIW As tImperialWeight
 
 If edtBMI.Text.Length = 0 Then
  Return
 End If
 
 If edtHeight.Text.Length = 0 Then
  Return
 End If
 
 If bMetric Then
  dWeightKg = dBMI * Power(dHeightCm / 100, 2)
 Else
  dWeightKg = dBMI * Power(ImperialHeight2Cm(iHeightFeet, dHeightInches) / 100, 2)
 End If
 
 If bMetric Then
  bCopyEditText = False
  edtWeight.Text = Round2(dWeightKg, 1)
 Else
  tIW = MetricWeight2Imperial(dWeightKg)
  edtWeight.Text = tIW.Stones
  
  bCopyEditText = False
  edtWeight2.Text = Round2(tIW.Pounds, 1)
  
  iWeightStones = tIW.Stones
  dWeightPounds = tIW.Pounds
 End If
 
End Sub

'to retain the real values, when entering directly in the edittext
Sub edtHeight_TextChanged(Old As String, New As String)
 
 If bCopyEditText Then
  If bMetric Then
   If New.Length > 0 Then
    dHeightCm = New
   Else
    dHeightCm = 0
   End If
  Else
   If New.Length > 0 Then
    iHeightFeet = New
   Else
    iHeightFeet = 0
   End If
  End If
 End If
 
 bCopyEditText = True
 
End Sub

Sub edtHeight2_TextChanged(Old As String, New As String)
 
 If bCopyEditText Then
  If New.Length > 0 Then
   dHeightInches = New
  Else
   dHeightInches = 0
  End If
 End If
 
 bCopyEditText = True
 
End Sub

Sub edtWeight_TextChanged(Old As String, New As String)
 
 If bCopyEditText Then
  If bMetric Then
   If New.Length > 0 Then
    dWeightKg = New
   Else
    dWeightKg = 0
   End If
  Else
   If New.Length > 0 Then
    iWeightStones = New
   Else
    iWeightStones = 0
   End If
  End If
 End If
 
 bCopyEditText = True
 
End Sub

Sub edtWeight2_TextChanged(Old As String, New As String)
 
 If bCopyEditText Then
  If New.Length> 0 Then
   dWeightPounds = New
  Else
   dWeightPounds = 0
  End If
 End If
 
 bCopyEditText = True
 
End Sub

Sub edtBMI_TextChanged(Old As String, New As String)
 
 If bCopyEditText Then
  If New.Length> 0 Then
   dBMI = New
  Else
   dBMI = 0
  End If
 End If
 
 bCopyEditText = True
 
End Sub

Sub chkMetric_Click() 'switch between Imperial and Metric
 
 Dim bImperial As Boolean
 Dim tIW As tImperialWeight
 Dim tIH As tImperialHeight
 
 bMetric = chkMetric.Checked
 bImperial = bMetric = False
 
 edtHeight2.Visible = bImperial
 edtWeight2.Visible = bImperial
 lblInch.Visible = bImperial
 lblPounds.Visible = bImperial
 
 If bMetric Then
  lblCm.Text = "Cm"
  lblKg.Text = "Kg"
  If edtHeight.Text.Length > 0 Or edtHeight2.Text.Length > 0 Then
   dHeightCm = (iHeightFeet * dFeet2Cm + dHeightInches * dInch2Cm)
   
   bCopyEditText = False
   edtHeight.Text = Round2(dHeightCm, 1)
   
  End If
  If edtWeight.Text.Length > 0 Or edtWeight2.Text.Length > 0 Then
   dWeightKg = iWeightStones * dStone2Kg + dWeightPounds * dPound2Kg
   
   bCopyEditText = False
   edtWeight.Text = Round2(dWeightKg, 1)

  End If
 Else
  lblCm.Text = "Feet"
  lblKg.Text = "Stone"
  If edtHeight.Text.Length > 0 Then
   tIH = MetricHeight2Imperial(dHeightCm)
   edtHeight.Text = tIH.Feet
   
   bCopyEditText = False
   edtHeight2.Text = Round2(tIH.Inches, 1)
   
   iHeightFeet = tIH.Feet
   dHeightInches = tIH.Inches
  End If
  If edtWeight.Text.Length > 0 Then
   tIW = MetricWeight2Imperial(dWeightKg)
   edtWeight.Text = tIW.Stones
   
   bCopyEditText = False
   edtWeight2.Text = Round2(tIW.Pounds,1)
   
   iWeightStones = tIW.Stones
   dWeightPounds = tIW.Pounds
  End If
 End If
 
 If (edtWeight.Text.Length > 0 And edtHeight.Text.Length > 0) Then
  btnCalculateBMI_Click
 End If
 
 SaveSetting("Metric", bMetric)

End Sub

RBS
 
Top