Over a million developers have joined DZone.

A Custom Float PropertyEditor

· Java Zone

Navigate the Maze of the End-User Experience and pick up this APM Essential guide, brought to you in partnership with CA Technologies

Both Java SE and the NetBeans Platform have default property editors for several primitive and common data types. These are suitable for most cases and most of us almost never need to worry about it. There are, however, those few moments where the one-size-fits-all approach does not actually fit. For instance, the default float editor is a text editor:

For general-purpose cases, this should be fine. Now, what if you want to restrict the values your property can have or have other control over it? Or simply give the user a more comfortable control for data input, like a JSpinner:

Most of what I'll show was taken from the NetBeans tutorials and Javadoc, so I will not extend in details what you can find easily there.

First, lets implement the PropertyEditor and the InlineEditor (don't forget to <Shift+Ctrl+I> to fix imports):

public abstract class FloatPropertyEditor
extends PropertyEditorSupport
implements ExPropertyEditor, InplaceEditor.Factory{

protected InplaceEditor ed = null;
protected SpinnerNumberModel model;

public FloatPropertyEditor(Object source, SpinnerNumberModel model) {
super(source);
this.model = model;
}

public FloatPropertyEditor(SpinnerNumberModel model) {
this.model = model;
}

@Override
public String getAsText() {
Float d = (Float)getValue();
if (d == null) {
return "0.0";
}
return NumberFormat.getNumberInstance().format(d.floatValue());
}

@Override
public void setAsText(String s) {
try {
setValue(new Float(
NumberFormat.getNumberInstance().parse(s).floatValue()));
} catch (ParseException ex) {
setValue(Float.valueOf(0.0f));
}
}

@Override
public void attachEnv(PropertyEnv env) {
env.registerInplaceEditorFactory(this);
}

@Override
public InplaceEditor getInplaceEditor() {
if (ed == null) {
ed = new FloatInplaceEditor(model);
}
return ed;
}

protected static class FloatInplaceEditor implements InplaceEditor {

private final JSpinner spinner;
private PropertyEditor editor = null;
private PropertyModel model;

public FloatInplaceEditor(SpinnerNumberModel model) {
this.spinner = new JSpinner(model);
}

@Override
public void connect(PropertyEditor propertyEditor, PropertyEnv env) {
editor = propertyEditor;
reset();
}

@Override
public JComponent getComponent() {
return spinner;
}

@Override
public void clear() {
//avoid memory leaks:
editor = null;
model = null;
}

@Override
public Object getValue() {
return spinner.getValue();
}

@Override
public void setValue(Object object) {
try {
spinner.setValue(object);
} catch (IllegalArgumentException e) {
spinner.setValue(null);
}
}

@Override
public boolean supportsTextEntry() {
return true;
}

@Override
public void reset() {
Float d = (Float) editor.getValue();
if (d != null) {
setValue(d);
}
}

@Override
public KeyStroke[] getKeyStrokes() {
return new KeyStroke[0];
}

@Override
public PropertyEditor getPropertyEditor() {
return editor;
}

@Override
public PropertyModel getPropertyModel() {
return model;
}

@Override
public void setPropertyModel(PropertyModel propertyModel) {
this.model = propertyModel;
}

@Override
public boolean isKnownComponent(Component component) {
return component == spinner || spinner.isAncestorOf(component);
}

@Override
public void addActionListener(ActionListener actionListener) {
//do nothing - not needed for this component
}

@Override
public void removeActionListener(ActionListener actionListener) {
//do nothing - not needed for this component
}
}
}


So far, we've been closely following the tutorials. Notice that the FloatPropertyEditor class is abstract. This is because PropertyEditor classes should have a default constructor (more on this below).

Not much of a gain, maybe, but now you have a JSpinner as an editor. Now imagine you are developing a 3D "bodies in space" application. You can select an item and you can change its dimensions and coordinates at will. Coordinates can have any real value, whether negative, zero, or positive (besides the practical dimensional limits your universe might have), while dimensions must be at least be zero.

This is where the abstract plays its role. By inheriting the class above, you can decide which values our properties can have. For coordinate properties, set this class as PropertyEditor:

public class CoordinateFloatPropertyEditor extends FloatPropertyEditor {

public CoordinateFloatPropertyEditor() {
super(new SpinnerNumberModel(0.0, -100000.0, 100000.0, 0.5));
}
}

And, for dimension properties, use this one, instead:
public class DimensionFloatPropertyEditor extends FloatPropertyEditor {

public DimensionFloatPropertyEditor() {
super(new SpinnerNumberModel(0.0, 0.09999, 100000.0, 0.5));
}
}


It's up to you to decide the maximum, minimum, and step values, and tweak this sample for your needs.

I want also to add that this is part of a real application, from where I was inspired to write this article. I'll be further extending this subject in the next posts.

Thanks for you attention.
Muchas gracias por su atención.

Thrive in the application economy with an APM model that is strategic. Be E.P.I.C. with CA APM.  Brought to you in partnership with CA Technologies.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}