接受JSpinner中的测量单位作为输入

问题描述 投票:1回答:1

我有一个数字JSpinner,它接受特定度量单位中的值。现在,我想要一种特殊的JSpinner行为:如果用户输入一个数字值并附加一个特定的度量单位字符串(例如“ inch”,“ pica”),则必须将输入的数字值转换为另一个值(取决于在单位字符串上)。当用户离开微调框(失去焦点)或以任何方式发生“ commitEdit”时,都必须进行此转换。

我已经尝试了几种变体:自定义文档过滤器,自定义格式实例和微调器的JFormattedTextField的自定义文本字段文档。但是我没有发现“钩住” JFormattedTextField的“ commitEdit”方法调用的任何可能性。

实现我的要求的最佳方法是什么?有一个简单的方法吗?

java swing units-of-measurement jspinner numberformatter
1个回答
0
投票

还有其他一些事情[使您可以在提交用户输入之前对其进行修改:它是commitEdit方法本身(在JFormattedTextFieldDefaultEditorJSpinner中)。在commitEdit内部,您可以看到stringToValueJFormattedTextField的方法AbstractFormatter被调用。这意味着,如果将自己的自定义AbstractFormatter赋予文本字段,则可以将任何字符串转换为值,并将值转换为任何字符串。这是发生异常的地方,以指示提交是否失败。

因此,按照您的要求,遵循处理不同单位的自定义AbstractFormatter

import java.text.ParseException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JFormattedTextField; import javax.swing.JFormattedTextField.AbstractFormatter; import javax.swing.JFormattedTextField.AbstractFormatterFactory; import javax.swing.JFrame; import javax.swing.JSpinner; import javax.swing.JSpinner.DefaultEditor; import javax.swing.SpinnerNumberModel; public class MilliMeterMain { public static enum Unit { MM(1), //Millimeters. IN(25.4), //Inches (1 inch == 25.4 mm). FT(25.4 * 12), //Feet (1 foot == 12 inches). YD(25.4 * 12 * 3); //Yards (1 yard == 3 feet). private final double factorToMilliMeters; //How much of this Unit composes a single millimeter. private Unit(final double factorToMilliMeters) { this.factorToMilliMeters = factorToMilliMeters; } public double getFactorToMilliMeters() { return factorToMilliMeters; } public double toMilliMeters(final double ammount) { return ammount * getFactorToMilliMeters(); } public double fromMilliMeters(final double ammount) { return ammount / getFactorToMilliMeters(); } } public static class UnitFormatter extends AbstractFormatter { private static final Pattern PATTERN; static { //Building the Pattern is not too tricky. It just needs some attention. final String blank = "\\p{Blank}"; //Match any whitespace character. final String blankGroupAny = "(" + blank + "*)"; //Match any whitespace character, zero or more times and group them. final String digits = "\\d"; //Match any digit. final String digitsGroup = "(" + digits + "+)"; //Match any digit, at least once and group them. final String digitsSuperGroup = "(\\-?" + digitsGroup + "\\.?" + digitsGroup + "?)"; //Matches for example "-2.4" or "2.4" or "2" or "-2" in the same group! //Create the pattern part which matches any of the available units... final Unit[] units = Unit.values(); final StringBuilder unitsBuilder = new StringBuilder(Pattern.quote("")); //Empty unit strings are valid (they default to millimeters). for (int i = 0; i < units.length; ++i) unitsBuilder.append('|').append(Pattern.quote(units[i].name())); final String unitsGroup = "(" + unitsBuilder + ")"; final String full = "^" + blankGroupAny + digitsSuperGroup + blankGroupAny + unitsGroup + blankGroupAny + "$"; //Compose full pattern. PATTERN = Pattern.compile(full); } private Unit lastUnit = Unit.MM; @Override public Object stringToValue(final String text) throws ParseException { if (text == null || text.trim().isEmpty()) throw new ParseException("Null or empty text.", 0); try { final Matcher matcher = PATTERN.matcher(text.toUpperCase()); if (!matcher.matches()) throw new ParseException("Invalid input.", 0); final String ammountStr = matcher.group(2), unitStr = matcher.group(6); final double ammount = Double.parseDouble(ammountStr); lastUnit = unitStr.trim().isEmpty()? null: Unit.valueOf(unitStr); return lastUnit == null? ammount: lastUnit.toMilliMeters(ammount); } catch (final IllegalArgumentException iax) { throw new ParseException("Failed to parse input \"" + text + "\".", 0); } } @Override public String valueToString(final Object value) throws ParseException { final double ammount = lastUnit == null? (Double) value: lastUnit.fromMilliMeters((Double) value); return String.format("%.4f", ammount).replace(',', '.') + ((lastUnit == null)? "": (" " + lastUnit.name())); } } public static class UnitFormatterFactory extends AbstractFormatterFactory { @Override public AbstractFormatter getFormatter(final JFormattedTextField tf) { if (!(tf.getFormatter() instanceof UnitFormatter)) return new UnitFormatter(); return tf.getFormatter(); } } public static void main(final String[] args) { final JSpinner spin = new JSpinner(new SpinnerNumberModel(0d, -1000000d, 1000000d, 1d)); //Default numbers in millimeters. ((DefaultEditor) spin.getEditor()).getTextField().setFormatterFactory(new UnitFormatterFactory()); final JFrame frame = new JFrame("JSpinner infinite value"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(spin); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }

我使用测量长度(毫米,英寸,英尺和码)的单位。但是您可以根据自己的需要进行调整。

注意,在以上实现中,SpinnerNumberModel仅知道毫米。 AbstractFormatter可以将毫米转换为其他单位,也可以转换成毫米(根据用户的输入)。这意味着当您将单位设置为

YD

(即码)时,模型仍将旋转毫米,但JFormattedTextField将以码的分数旋转。试试看,了解我的意思。这意味着getValue() / JSpinnerSpinnerNumberModel将始终返回毫米的量,而不管文本字段中的单位是什么(AbstractFormatter始终进行转换)。作为第二种情况,如果需要,可以将转换移到AbstractFormatter之外。例如,您可以让用户在微调器中输入一个始终与测量单位无关的值。这样,用户始终会看到值旋转等于1的步长(在此示例中),同时AbstractFormatter将保留用户设置为旋转器的最后一个单位的属性。因此,现在当您从JSpinner / SpinnerNumberModel中获取值时,您将获得一个与单位无关的数字,然后使用设置为AbstractFormatter的最后一个单位来确定用户要使用的单位。这与使用微调器有些不同,可能更方便。

这是第二种情况的代码:

import java.text.ParseException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JFormattedTextField; import javax.swing.JFormattedTextField.AbstractFormatter; import javax.swing.JFormattedTextField.AbstractFormatterFactory; import javax.swing.JFrame; import javax.swing.JSpinner; import javax.swing.JSpinner.DefaultEditor; import javax.swing.SpinnerNumberModel; public class StepMain { public static enum Unit { MM, //Milimeters. IN, //Inches (1 inch == 25.4 mm). FT, //Feet (1 foot == 12 inches). YD; //Yards (1 yard == 3 feet). } public static class UnitFormatter extends AbstractFormatter { private static final Pattern PATTERN; static { //Building the Pattern is not too tricky. It just needs some attention. final String blank = "\\p{Blank}"; //Match any whitespace character. final String blankGroupAny = "(" + blank + "*)"; //Match any whitespace character, zero or more times and group them. final String digits = "\\d"; //Match any digit. final String digitsGroup = "(" + digits + "+)"; //Match any digit, at least once and group them. final String digitsSuperGroup = "(\\-?" + digitsGroup + "\\.?" + digitsGroup + "?)"; //Matches for example "-2.4" or "2.4" or "2" or "-2" in the same group! //Create the pattern part which matches any of the available units... final Unit[] units = Unit.values(); final StringBuilder unitsBuilder = new StringBuilder(Pattern.quote("")); //Empty unit strings are valid (they default to milimeters). for (int i = 0; i < units.length; ++i) unitsBuilder.append('|').append(Pattern.quote(units[i].name())); final String unitsGroup = "(" + unitsBuilder + ")"; final String full = "^" + blankGroupAny + digitsSuperGroup + blankGroupAny + unitsGroup + blankGroupAny + "$"; //Compose full pattern. PATTERN = Pattern.compile(full); } private Unit lastUnit = Unit.MM; @Override public Object stringToValue(final String text) throws ParseException { if (text == null || text.trim().isEmpty()) throw new ParseException("Null or empty text.", 0); try { final Matcher matcher = PATTERN.matcher(text.toUpperCase()); if (!matcher.matches()) throw new ParseException("Invalid input.", 0); final String ammountStr = matcher.group(2), unitStr = matcher.group(6); final double ammount = Double.parseDouble(ammountStr); lastUnit = Unit.valueOf(unitStr); return ammount; } catch (final IllegalArgumentException iax) { throw new ParseException("Failed to parse input \"" + text + "\".", 0); } } @Override public String valueToString(final Object value) throws ParseException { return String.format("%.3f", value).replace(',', '.') + ' ' + lastUnit.name(); } } public static class UnitFormatterFactory extends AbstractFormatterFactory { @Override public AbstractFormatter getFormatter(final JFormattedTextField tf) { if (!(tf.getFormatter() instanceof UnitFormatter)) return new UnitFormatter(); return tf.getFormatter(); } } public static void main(final String[] args) { final JSpinner spin = new JSpinner(new SpinnerNumberModel(0d, -1000000d, 1000000d, 0.001d)); ((DefaultEditor) spin.getEditor()).getTextField().setFormatterFactory(new UnitFormatterFactory()); final JFrame frame = new JFrame("JSpinner infinite value"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(spin); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }

关于您所说的语言环境,如果我理解正确,您希望逗号和点都在同一微调器中运行吗?如果是这样,您可以检查答案here恰好与此有关。在那种情况下,仍然可以通过使用自定义AbstractFormatter解决问题。
© www.soinside.com 2019 - 2024. All rights reserved.