欢迎就创建我自己的 Swing 组件提出建议
Posted
技术标签:
【中文标题】欢迎就创建我自己的 Swing 组件提出建议【英文标题】:Advice welcomed on creating my own Swing component 【发布时间】:2011-01-31 11:04:45 【问题描述】:最近I asked 是绑定到 BigDecimal 变量(具有一些特定编辑属性)的最佳 Swing 组件。事实证明,没有一个标准的 Swing 组件完全适合我,我在那里找到的第三方 Swing 组件库也不适合我。所以我决定创建自己的 Swing 组件。
组件说明:
我想扩展JTextField 或JFormattedTextField,这样我的新组件就可以轻松绑定到BigDecimal 变量。
组件将具有可自定义的 scale 和 length 属性。
行为:
绘制组件时,它仅显示小数点和右侧scale位的空格。
当组件获得焦点时,插入符号应位于小数点左侧。当用户键入数字时(忽略任何其他字符),它们出现在插入符号的左侧,仅接受 length - scale 数字,输入的任何其他数字都将被忽略为整数部分已满。每当用户键入小数点时,插入符号就会移动到小数点的右侧。以下输入的数字显示在小数部分,只有 scale 数字被认为是任何其他输入的数字都将被忽略,因为小数部分已满。此外,当用户键入小数点后的数字时,应显示千位分隔符。
我还希望能够在JTable 中将组件用作Cell Editor(无需编码两次)。
在组件上调用 getValue() 方法应该会产生表示刚刚输入的数字的 BigDecimal。
我从未创建过自己的 Swing 组件;我几乎没用过标准的。因此,我将不胜感激有关创建所描述组件的任何好的教程/信息/提示。 This 是我目前唯一得到的。
提前致谢。
【问题讨论】:
交叉发布:coderanch.com/t/488720/Swing-AWT-SWT-JFace/java/… 【参考方案1】:我喜欢您引用的 Grouchnikov 文章,但我不确定您是否想要更改 UI 委托。因为这将是一个不可变对象的视图,所以我更喜欢组合而不是继承。我倾向于将您描述的组件视为renderer,如example 所示。您可以添加InputVerifier
或DocumwntListener
以获得所需的验证。
附录:这是一个使用JFormattedTextField
和MaskFormatter
的示例。您需要调整格式掩码以匹配您的比例和长度。
public class TableGrid extends JPanel
private DecimalFormat df;
private MaskFormatter mf;
private JFormattedTextField tf;
public TableGrid()
df = new DecimalFormat("0.00");
try
mf = new MaskFormatter("#.##");
catch (ParseException ex)
ex.printStackTrace();
tf = new JFormattedTextField(mf);
TableModel dataModel = new TableModel();
JTable table = new JTable(dataModel);
table.setCellSelectionEnabled(true);
table.setRowHeight(32);
table.setDefaultRenderer(BigDecimal.class, new DecRenderer(df));
table.setDefaultEditor(BigDecimal.class, new DecEditor(tf, df));
this.add(table);
private static class TableModel extends AbstractTableModel
private static final int SIZE = 4;
private BigDecimal[][] matrix = new BigDecimal[SIZE][SIZE];
public TableModel()
for (Object[] row : matrix)
Arrays.fill(row, BigDecimal.valueOf(0));
@Override
public int getRowCount()
return SIZE;
@Override
public int getColumnCount()
return SIZE;
@Override
public Object getValueAt(int row, int col)
return matrix[row][col];
@Override
public void setValueAt(Object value, int row, int col)
matrix[row][col] = (BigDecimal) value;
@Override
public Class<?> getColumnClass(int col)
return BigDecimal.class;
@Override
public boolean isCellEditable(int row, int col)
return true;
private static class DecRenderer extends DefaultTableCellRenderer
DecimalFormat df;
public DecRenderer(DecimalFormat df)
this.df = df;
this.setHorizontalAlignment(JLabel.CENTER);
this.setBackground(Color.lightGray);
this.df.setParseBigDecimal(true);
@Override
protected void setValue(Object value)
setText((value == null) ? "" : df.format(value));
private static class DecEditor extends DefaultCellEditor
private JFormattedTextField tf;
private DecimalFormat df;
public DecEditor(JFormattedTextField tf, DecimalFormat df)
super(tf);
this.tf = tf;
this.df = df;
tf.setHorizontalAlignment(JFormattedTextField.CENTER);
@Override
public Object getCellEditorValue()
try
return new BigDecimal(tf.getText());
catch (NumberFormatException e)
return BigDecimal.valueOf(0);
@Override
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column)
tf.setText((value == null) ? "" : df.format((BigDecimal) value));
if (isSelected) tf.selectAll();
return tf;
public static void main(String[] args)
EventQueue.invokeLater(new Runnable()
@Override
public void run()
JFrame f = new JFrame("TableGrid");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(new TableGrid());
f.pack();
f.setVisible(true);
);
【讨论】:
谢谢,该示例的行为与我描述的不完全一样,但这是一个很好的起点。【参考方案2】:使用您喜欢的任何组件并注册 KeyListener 来拒绝字符以匹配您的行为。添加 getValue() 和 setValue 以轻松获取/设置 BiDecimal 和其他一些方法,所有绘画将由任何 JTextComponent 提供。
【讨论】:
你所说的“所有的绘画”将由任何 JTextComponent 提供。”是什么意思? 就像听起来一样:) 最后,您希望拥有一个编辑“文本”的组件,该组件仅包含一些字符和某种插入行为。因此,使用 KeyListener 并分析光标(或左右)下的字符,您将决定是否接受击键,如果是,则将其插入到哪里。最后,您的需求只讨论了组件在插入数据时的行为方式,而与绘画无关。让 JTextField 来绘画并改变它接受击键的行为。 好的。还有一件事:你确定我必须注册一个 KeyListener 吗?如何“拒绝与我想要的字符不匹配的字符”?我认为在组件的文档中添加 DocumentListener 会更合适,但你告诉我……你似乎比我更了解这一点。 使用 DocumentListener 更改已经存在,您只想接受一些输入。在文档更改后通知并通过删除字符将文档更改回来是没有意义的。是的,注册一个 KeyListener 和组合 cu 光标位置(查看您在文档中的位置等),您可以获得所需的行为。不要忘记使用 KeyEvents 来消除不需要的击键。 但是在keylistener执行keytyped、keypressed和keyreleased方法之后,文档被修改了。我怎样才能告诉文件不要从 keylistener 中改变?那是我不明白的。也许答案是扩展 PlainDocument,这样我就可以在输入发生时控制输入(insertString 方法),而不是之前(与 keylistener 一样),也不是之后(与 DocumentListener 一样)。以上是关于欢迎就创建我自己的 Swing 组件提出建议的主要内容,如果未能解决你的问题,请参考以下文章