Reputation: 191
How to create a Slider to set a float value, and an EditText reflecting that float value, leave both of them connected to a ViewModel object float value, that is updated on the run. The float value should be possible to set both from the Slider and the EditText box.
I find it cumbersome to
Upvotes: 0
Views: 81
Reputation: 191
The solution I found was to 1) skip the Converter and use a BindingAdapter and InverseBindingAdapter to handle the float value in the EditText android:text field, and 2) occasionally avoid updating the setText in the BindingAdapter (value passed from view model, to setText in the VIEW) - this will leave a half written float (string) intact.
essense from the xml file:
<data>
<variable name="model" type="com....viewmodel.MyViewModel"/>
</data>
...
<EditText
android:id="@+id/someId"
...
android:text="@={model.count}"
android:selectAllOnFocus="true"
android:digits="0123456789."/>
<com.google.android.material.slider.Slider
android:id="@+id/someSlider"
...
android:valueTo="@{model.max}"
android:value="@={model.count}" />
This is included to display the 2 way binder float connection to the model object.
Essence from my SliderAdapter.kt file:
@BindingAdapter("android:valueAttrChanged")
fun setSliderListeners(slider: Slider, attrChange: InverseBindingListener) {
slider.addOnChangeListener { _, _, _ ->
attrChange.onChange()
}
}
@InverseBindingAdapter(attribute = "android:value")
fun getSliderValue(slider: Slider) = slider.value
This binder will process the Slider value set/get in the xml .
The following Binder will handle the value set/get in the EditText, before value is passed to the model object. Avoid updating the text in the gui when not needed is crucial.
@BindingAdapter("android:text")
fun setText(view: EditText, value: Float?) {
if (value == null) return
var decimals: Int = (value * 100).roundToInt() % 100
decimals = if (decimals == 0) 0 else
if (decimals % 10 == 0) 1 else 2
val oldVal = view.text.toString().toFloatOrNull() ?: 0F
val valRounded : Float = (value * 100)/100
if (valRounded != oldVal)
view.setText(String.format("%." + decimals + "f", value))
// Avoid updating the text if last character was a '.'
view.setSelection(view.text.length);
// This line will place the marker at the end of the written text.
}
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
fun getTextString(view: EditText): Float {
return view.text.toString().toFloatOrNull() ?: 0F
}
And finally, if anyone is still with me, the model kotlin file:
class MyViewModel : ViewModel() {
val max : Float = 10.5F
val count = MutableLiveData(2.0f)
get() {
if(field.value!! > max)
field.value = max
return field
}
}
Voila, its now possible to enter a half written float:
Upvotes: 0