FormattedTextField

@Composable
fun FormattedTextField(value: Any?, onValueChange: (Any?) -> Unit, modifier: SwingModifier = SwingModifier, formatterFactory: JFormattedTextField.AbstractFormatterFactory? = null, onEditValidChange: (Boolean) -> Unit = {}, focusLostBehavior: Int = JFormattedTextField.COMMIT_OR_REVERT, columns: Int = 0, editable: Boolean = true)

A single line of text standing for a typed value: a number, a date, or input matching a fixed mask. The JFormattedTextField renders the value as formatted text and parses what the user types back into a value of that type.

The field parses and formats through formatterFactory, which produces the formatter that maps between the typed value and the displayed text (e.g. a NumberFormatter, a DateFormatter, or a MaskFormatter for a fixed mask). With no factory the field falls back to the platform default, which is derived from the class of value - the class the field edits in: what it renders, and what a commit is parsed back to. A value declared in another class derives that default again, so a field declared an Int and later a Long commits Longs from then on.

value is the committed, typed value (an Int, a Date, a String, ...); onValueChange fires once per value the field commits from an edit, carrying the newly parsed value. Text the user types that does not parse is not committed and produces no callback until it becomes valid. A commit that leaves the value where it was carries nothing new and is not reported. Applying value is not itself reported, so a callback that writes value back does not loop.

This field is strictly controlled: a value the field commits that onValueChange does not answer with a matching value is settled back onto the declared value on the very next pass, so the field never ends up holding a value the caller has not adopted. A value the field has already committed is left alone rather than written again - the characters typed since that commit stay.

FormattedTextField(
value = amount,
formatterFactory = remember {
DefaultFormatterFactory(NumberFormatter().apply { valueClass = Int::class.javaObjectType })
},
onValueChange = { amount = it as Int },
)

A NumberFormatter's valueClass decides the type onValueChange receives; set to Int::class.javaObjectType here, it is what makes the committed value the Int the example casts to.

Installing a formatter re-renders the committed value through it, which replaces characters the user has typed but not committed. A formatterFactory is installed whenever a different instance is declared, so hold one instance across recompositions (e.g. remember { ... }) and supply a new one only where the formatting is meant to change.

The text the user is part way through typing need not parse, and while it does not the field holds its previous value: onEditValidChange reports that, so a form can mark the field or hold its submit button back. Drive the field with the FormattedValueState overload to read that as state instead, and to take a part-typed edit on demand rather than waiting for the field's own focus-lost behavior.

Parameters

value

the committed, typed value

onValueChange

callback invoked with the parsed value when the field commits an edit; applying value is not itself reported

modifier

the SwingModifier applied to the underlying component

formatterFactory

the factory producing the field's formatter, or null for the default

onEditValidChange

callback invoked with whether the text now parses, each time that changes; nothing is reported by default

focusLostBehavior

what to do with a partial edit when the field loses focus (a FocusLostBehavior JFormattedTextField constant); COMMIT_OR_REVERT by default, which commits an edit that parses and discards one that does not, restoring the last committed value

columns

the preferred width in columns; 0 by default, sizing to the content

editable

whether the user can type into the field; true by default

See also


@Composable
fun FormattedTextField(value: Any?, valuePropertyChangeListener: PropertyChangeListener, modifier: SwingModifier = SwingModifier, formatterFactory: JFormattedTextField.AbstractFormatterFactory? = null, onEditValidChange: (Boolean) -> Unit = {}, focusLostBehavior: Int = JFormattedTextField.COMMIT_OR_REVERT, columns: Int = 0, editable: Boolean = true)

A FormattedTextField driven by a raw PropertyChangeListener (bound to the value property) instead of an onValueChange lambda. The listener is attached as-is and removed on the same instance; pass a stable instance (e.g. remember {}) to avoid churn. Being attached as-is, it is notified of every change to the value property, including the one that applies value.

This field is strictly controlled: a value the field commits that is not followed by value moving to match is settled back onto the declared value on the very next pass, so the field never ends up holding a value the caller has not adopted.

Installing a formatter re-renders the committed value through it, which replaces characters the user has typed but not committed. A formatterFactory is installed whenever a different instance is declared, so hold one instance across recompositions (e.g. remember { ... }) and supply a new one only where the formatting is meant to change.

Parameters

value

the committed, typed value

valuePropertyChangeListener

the listener notified when the committed value changes

modifier

the SwingModifier applied to the underlying component

formatterFactory

the factory producing the field's formatter, or null for the default

onEditValidChange

callback invoked with whether the text now parses, each time that changes; nothing is reported by default

focusLostBehavior

what to do with a partial edit when the field loses focus (a FocusLostBehavior JFormattedTextField constant); COMMIT_OR_REVERT by default, which commits an edit that parses and discards one that does not, restoring the last committed value

columns

the preferred width in columns; 0 by default, sizing to the content

editable

whether the user can type into the field; true by default

See also


@Composable
fun FormattedTextField(state: FormattedValueState, modifier: SwingModifier = SwingModifier, formatterFactory: JFormattedTextField.AbstractFormatterFactory? = null, focusLostBehavior: Int = JFormattedTextField.COMMIT_OR_REVERT, columns: Int = 0, editable: Boolean = true)

A FormattedTextField driven by a FormattedValueState. The field renders the state's value and commits into it, and reports through the state whether the characters it currently shows parse. The state is the single source of truth; there is no onValueChange and no onEditValidChange.

val amount = rememberFormattedValueState(10)
FormattedTextField(state = amount, formatterFactory = factory)
Button("Save", onClick = { if (amount.commit()) save(amount.value) })

Installing a formatter re-renders the committed value through it, which replaces characters the user has typed but not committed. A formatterFactory is installed whenever a different instance is declared, so hold one instance across recompositions (e.g. remember { ... }) and supply a new one only where the formatting is meant to change.

Parameters

state

the hoistable value state the field renders and drives

modifier

the SwingModifier applied to the underlying component

formatterFactory

the factory producing the field's formatter, or null for the default

focusLostBehavior

what to do with a partial edit when the field loses focus (a FocusLostBehavior JFormattedTextField constant); COMMIT_OR_REVERT by default, which commits an edit that parses and discards one that does not, restoring the last committed value

columns

the preferred width in columns; 0 by default, sizing to the content

editable

whether the user can type into the field; true by default

See also