我试图实现以下目标。
我想使用一个使用数字作为数据的JSpinner,但是我想让它像这样渲染:“6/10”,即“value / maximum”,默认的JSpinner只显示“value” 。当然,我使用SpinnerNumberModel只能将整数用作数据。
与JCombobox渲染器和/或编辑器,JTable渲染器和/或编辑器相比,我一直在寻找模拟行为......但似乎JSpinner不能以这种方式工作。我找到this example,用于微调器内的自定义内容。该示例使用JLabel来显示图标,但在我的情况下,我必须能够在点击 Enter 之后编辑内容并检查输入。
所以这就是我基本上做的事情:
JSpinner spinner = new JSpinner(new SpinnerNumberModel(1,1,10,1));
spinner.setEditor(new CustomSpinnerEditor(spinner));
CustomSpinnerEditor正在扩展JTextField。我在其上释放了一些inputmap / actionmap(对于 Enter 行为),将一个ChangeListener添加到微调器,以便在用户点击箭头时刷新文本字段内容,添加了一个FocusListener以选择全部关于focusGained的文本并验证focusLost上的输入,......并且它可以正常工作。
所以问题不是让它发挥作用。问题是,这对于非常简单的事情来说太过分了,而且我可能会因为没有使用默认编辑器而丢失一些功能。如果组件只提供了在其上设置渲染器的功能,我就会有一个钩子,我只需返回一个带有正确String的JLabel,就像我对JComboBox一样。
有没有更简单的方法来实现我想要的目标?
答案 0 :(得分:2)
您可以避免创建专用编辑器,但我不知道它是否变得更简单:使用
检索默认编辑器的文本字段JFormattedTextField f = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
然后在该文本字段上设置格式化程序工厂:
f.setFormatterFactory(new AbstractFormatterFactory() {
@Override
public AbstractFormatter getFormatter(JFormattedTextField tf) {
// return your formatter
}
});
getFormatter
方法返回的格式化程序必须使用stringToValue
和valueToString
方法在您的值和字符串表示形式之间进行转换。
答案 1 :(得分:1)
我不知道你是否会认为这更简单。
我创建了一个Renderer
类,其中包含要显示的值和值字符串。
在自定义微调器模型中,我创建了一个Renderer
实例列表,并导航了列表。
我将这些类放在一起,这样我就可以复制并粘贴一段代码。这三个类应保存在不同的源模块中。
这是做你想做的事的一种方法。
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractSpinnerModel;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class SpinnerTesting implements Runnable {
@Override
public void run() {
JFrame frame = new JFrame("JSpinner Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
final JSpinner spinner = new JSpinner(new CustomSpinnerModel(4, 1, 10,
1));
setBorder(spinner);
spinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent event) {
Renderer renderer = (Renderer) spinner.getValue();
System.out.println(renderer.getValue());
}
});
mainPanel.add(spinner);
frame.add(mainPanel);
frame.setSize(new Dimension(300, 100));
frame.setVisible(true);
}
private void setBorder(JSpinner spinner) {
Border lineBorder = BorderFactory.createLineBorder(Color.BLACK);
Border insetBorder = BorderFactory.createEmptyBorder(0, 10, 0, 10);
spinner.setBorder(BorderFactory.createCompoundBorder(lineBorder,
insetBorder));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new SpinnerTesting());
}
public class CustomSpinnerModel extends AbstractSpinnerModel {
private int value;
private int minimum;
private int maximum;
private int stepSize;
private int listIndex;
private List<Renderer> spinnerList;
public CustomSpinnerModel(int value, int minimum, int maximum,
int stepSize) throws IllegalArgumentException {
if (!((minimum <= value) && (value <= maximum))) {
throw new IllegalArgumentException(
"(minimum <= value <= maximum) is false");
}
this.value = value;
this.minimum = minimum;
this.maximum = maximum;
this.stepSize = stepSize;
this.spinnerList = new ArrayList<Renderer>();
setSpinnerList();
}
private void setSpinnerList() {
int index = 0;
for (int i = minimum; i <= maximum; i += stepSize) {
Renderer renderer = new Renderer(i, maximum);
if (i == value) {
listIndex = index;
}
spinnerList.add(renderer);
index++;
}
}
@Override
public Object getNextValue() {
listIndex = Math.min(++listIndex, (spinnerList.size() - 1));
fireStateChanged();
return spinnerList.get(listIndex);
}
@Override
public Object getPreviousValue() {
listIndex = Math.max(--listIndex, 0);
fireStateChanged();
return spinnerList.get(listIndex);
}
@Override
public Object getValue() {
return spinnerList.get(listIndex);
}
@Override
public void setValue(Object object) {
}
}
public class Renderer {
private int value;
private String valueString;
public Renderer(int value, int maximum) {
this.value = value;
this.valueString = value + " / " + maximum;
}
public int getValue() {
return value;
}
public String getValueString() {
return valueString;
}
@Override
public String toString() {
return valueString + " ";
}
}
}
答案 2 :(得分:0)
是的,有一种更简单的方法......
为什么要填充自定义编辑器的动作地图而不仅仅是编辑DefaultEditor
的动作地图?
让DefaultEditor
JFormattedTextField
为input notify事件提供自定义操作:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.text.TextAction;
public class OnEnterSpinner extends JPanel {
private final JSpinner spin;
private final JSpinner.DefaultEditor editor;
private final JFormattedTextField field;
private final class OnEnterAction extends TextAction {
private OnEnterAction() {
super(JTextField.notifyAction); //The name of the TextAction. Let's say it is its key.
}
@Override
public void actionPerformed(final ActionEvent actevt) {
if (getFocusedComponent() == field)
System.out.println("The user pressed ENTER for: \"" + field.getText() + "\".");
}
}
private OnEnterSpinner() {
spin = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); //Your values here. Let's say for now this is a spinner for integers.
spin.setPreferredSize(new Dimension(100, 20));
//We need a DefaultEditor (to be able to obtain the JFormattedTextField and customize it).
editor = new JSpinner.DefaultEditor(spin);
field = editor.getTextField();
field.setEditable(true); //Allow user input (obvious reasons).
//And here, you register the custom action!
field.getActionMap().put(JTextField.notifyAction, new OnEnterAction()); //I found the proper key for the map: "notifyAction".
spin.setEditor(editor);
add(spin);
}
public static void main(final String[] args) {
final JFrame frame = new JFrame("OnEnterSpinner demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new OnEnterSpinner());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
此外,如果您需要通过提供给JFormattedTextField
的自定义AbstractFormatter
检查输入值的有效性,请在输入时调用JSpinner#commitEdit()
:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.text.TextAction;
public class CommitOnEnterSpinner extends JPanel {
private final JSpinner spin;
private final JSpinner.DefaultEditor editor;
private final JFormattedTextField field;
private final class OnEnterAction extends TextAction {
private OnEnterAction() {
super(JTextField.notifyAction);
}
@Override
public void actionPerformed(final ActionEvent actevt) {
if (getFocusedComponent() == field) { //This check may be redundant in this case.
System.out.println("The user pressed ENTER for: \"" + field.getText() + "\".");
try {
//Commit the edits, upon carriage return:
spin.commitEdit();
//If successfull, then handle it as desired:
JOptionPane.showMessageDialog(null, "Yes! This is a valid value.", "OK", JOptionPane.PLAIN_MESSAGE);
}
catch (final ParseException pe) {
//If failed, then handle the unaccepted value:
JOptionPane.showMessageDialog(null, "Sorry, \"" + field.getText() + "\" is not a valid value.", "Oups!", JOptionPane.INFORMATION_MESSAGE);
}
}
}
}
private CommitOnEnterSpinner() {
spin = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 1)); //Your values here. Let's say for now this is a spinner for integers.
spin.setPreferredSize(new Dimension(100, 20));
//We need a DefaultEditor (to be able to obtain the JFormattedTextField and customize it).
editor = new JSpinner.DefaultEditor(spin);
field = editor.getTextField();
field.setEditable(true); //Allow user input (obvious reasons).
//And here, you register the custom action!
field.getActionMap().put(JTextField.notifyAction, new OnEnterAction());
spin.setEditor(editor);
add(spin);
}
public static void main(final String[] args) {
final JFrame frame = new JFrame("CommitOnEnterSpinner demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new CommitOnEnterSpinner());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}