1

最近我问到哪个 Swing 组件最好绑定到 BigDecimal 变量(具有一些特定的编辑属性)。事实证明,没有一个标准的 Swing 组件完全适合我,我在那里找到的第三方 Swing 组件库也不适合我。所以我决定创建自己的 Swing 组件。

组件说明:

我想扩展JTextFieldJFormattedTextField,因此我的新组件可以轻松绑定到BigDecimal变量。

该组件将具有可自定义的比例长度属性。

行为:

绘制组件时,它仅显示小数点和右侧的刻度数字空格。

当组件获得焦点时,插入符号应位于小数点左侧。当用户键入数字(任何其他字符被忽略)时,它们出现在插入符号的左侧,只接受长度-比例数字,任何其他键入的数字都被忽略,因为整数部分已满。每当用户键入小数点时,插入符号就会移动到小数点的右侧。以下键入的数字显示在小数部分中,仅考虑刻度数字,因为小数部分已满,任何其他键入的数字都将被忽略。此外,千位分隔符应显示为用户键入的数字保留到小数点。

我还希望能够将该组件用作JTable中的单元格编辑器(无需对其进行两次编码)。

在组件上调用 getValue() 方法应该会产生表示刚刚输入的数字的 BigDecimal。


我从未创建过自己的 Swing 组件;我几乎没用过标准的。因此,我将不胜感激有关创建所描述组件的任何好的教程/信息/提示。是我到目前为止唯一得到的东西。

提前致谢。

4

2 回答 2

3

我喜欢您引用的Grouchnikov文章,但我不确定您是否想要更改 UI 委托。由于这将是一个不可变对象的视图,因此我更喜欢组合而不是继承。我倾向于将您描述的组件视为渲染器,如本示例所示。您可以添加InputVerifierorDocumwntListener来获得所需的验证。

附录:这是一个使用JFormattedTextFieldMaskFormatter. 您需要调整格式掩码以匹配您的比例和长度。

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);
            }
        });
    }
}
于 2010-03-24T21:07:39.100 回答
2

使用您喜欢的任何组件并注册 KeyListener 来拒绝字符以匹配您的行为。添加 getValue() 和 setValue 以轻松获取/设置 BiDecimal 和其他一些方法,所有绘画都将由任何 JTextComponent 提供。

于 2010-03-24T21:09:36.950 回答