欢迎就创建我自己的 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 变量。

组件将具有可自定义的 scalelength 属性。

行为:

绘制组件时,它仅显示小数点和右侧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 所示。您可以添加InputVerifierDocumwntListener 以获得所需的验证。

附录:这是一个使用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);
            
        );
    

【讨论】:

谢谢,该示例的行为与我描述的不完全一样,但这是一个很好的起点。【参考方案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 组件提出建议的主要内容,如果未能解决你的问题,请参考以下文章

Java Swing Jtable 单元格不可编辑

Java Swing关于界面刷新的问题!!

java awt和swing包自带吗

推荐 Java Swing 组件库

如何让 Swing 将我自己的 HTMLEditorKit 用于 JLabel/JButton/等

java - swing - 组件不听边界