如何在Swing中实现动态GUI

12

首先,抱歉发布了这样一个可能有点过于具体的问题,但我对Swing不是很熟悉,也找不到符合我的需求的好例子。

因此,我正在尝试找出在Swing中实现动态GUI以选择过滤条件的最佳方法:

要实现的GUI模型

底层模型是一个包含可以否定的(即用NOT前缀应用的)一系列条件的类,并指示这些条件是否与AND或OR结合的属性。

GUI将允许用户添加、更改或删除条件,并选择组合运算符(and/or)。第一个条件自然不会有组合选择器,第三个及后续条件将简单地使用与第二个相同的组合运算符。

右侧的X按钮将用于删除一个条件。当按下添加按钮时,新的组件行将添加到底部。随着所做更改的反映,这些更改将在底层模型中反映出来。

当然,我可以通过向JPanel添加组件并相应地更新模型来相当“原始”地实现这一点,但我更喜欢更整洁的解决方案,例如由TableModel提供的解决方案。

因此,我想知道使用自定义的TableModel和TableCellRenderer/Editor的表格是否是最佳方法,或者是否有更好的方法来实现这样的内容。如果表格确实是最佳方法,我将感激一些使用TableCellRenderers或-Editors来完成此操作的指针。

提前感谢。


参考资料,"criteria"的单数形式是"criterion"。另请参阅criterium,这是一种自行车比赛。 - trashgod
4
翻译:哎呀,这不是我的母语 :) - Rolf
4个回答

18

这只是一个示例,所有内容都是硬编码的,便于理解。

编辑:

正如kleopatra指出的那样,将JTable#fireTableDataChanged()从ActionListener移动到TableModel中,并修正所有以小写字母开头的类名。

import java.awt.*;
import java.awt.event.*;
import java.util.EventObject;
import javax.swing.*;
import javax.swing.table.*;

public class ComponentTableTest {

    private JFrame frame;
    private JTable CompTable = null;
    private CompTableModel CompModel = null;
    private JButton addButton = null;

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ComponentTableTest().makeUI();
            }
        });
    }

    public void makeUI() {
        CompTable = CreateCompTable();
        JScrollPane CompTableScrollpane = new JScrollPane(CompTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        JPanel bottomPanel = CreateBottomPanel();
        frame = new JFrame("Comp Table Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(CompTableScrollpane, BorderLayout.CENTER);
        frame.add(bottomPanel, BorderLayout.SOUTH);
        frame.setPreferredSize(new Dimension(800, 400));
        frame.setLocation(150, 150);
        frame.pack();
        frame.setVisible(true);
    }

    public JTable CreateCompTable() {
        CompModel = new CompTableModel();
        CompModel.addRow();
        JTable table = new JTable(CompModel);
        table.setRowHeight(new CompCellPanel().getPreferredSize().height);
        table.setTableHeader(null);
        CompCellEditorRenderer compCellEditorRenderer = new CompCellEditorRenderer();
        table.setDefaultRenderer(Object.class, compCellEditorRenderer);
        table.setDefaultEditor(Object.class, compCellEditorRenderer);
        return table;
    }

    public JPanel CreateBottomPanel() {
        addButton = new JButton("Add Comp");
        addButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                Object source = ae.getSource();

                if (source == addButton) {
                    CompModel.addRow();
                    //CompModel.fireTableDataChanged(); // moved to TableModel
                }
            }
        });
        JPanel panel = new JPanel(new GridBagLayout());
        panel.add(addButton);
        return panel;
    }
}

class CompCellEditorRenderer extends AbstractCellEditor implements TableCellRenderer, TableCellEditor {

    private static final long serialVersionUID = 1L;
    private CompCellPanel renderer = new CompCellPanel();
    private CompCellPanel editor = new CompCellPanel();

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        renderer.setComp((Comp) value);
        return renderer;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        editor.setComp((Comp) value);
        return editor;
    }

    @Override
    public Object getCellEditorValue() {
        return editor.getComp();
    }

    @Override
    public boolean isCellEditable(EventObject anEvent) {
        return true;
    }

    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        return false;
    }
}

class CompTableModel extends DefaultTableModel {

    private static final long serialVersionUID = 1L;

    @Override
    public int getColumnCount() {
        return 1;
    }

    public void addRow() {
        super.addRow(new Object[]{new Comp(0, 0, "", "")});
        //super.fireTableDataChanged();
    }
}

class Comp {

    int type;
    int relation;
    String lower;
    String upper;

    public Comp(int type, int relation, String lower, String upper) {
        this.type = type;
        this.relation = relation;
        this.lower = lower;
        this.upper = upper;
    }
}

class CompCellPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    private JLabel labelWith = new JLabel("With ");
    private JComboBox typeCombo = new JComboBox(new Object[]{"height", "length", "volume"});
    private JComboBox relationCombo = new JComboBox(new Object[]{"above", "below", "between"});
    private JTextField lowerField = new JTextField();
    private JLabel labelAnd = new JLabel(" and ");
    private JTextField upperField = new JTextField();
    private JButton removeButton = new JButton("remove");

    CompCellPanel() {
        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        relationCombo.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                enableUpper(relationCombo.getSelectedIndex() == 2);
            }
        });
        enableUpper(false);
        removeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JTable table = (JTable) SwingUtilities.getAncestorOfClass(JTable.class, (Component) e.getSource());
                int row = table.getEditingRow();
                table.getCellEditor().stopCellEditing();
                ((DefaultTableModel) table.getModel()).removeRow(row);
            }
        });
        add(labelWith);
        add(typeCombo);
        add(relationCombo);
        add(lowerField);
        add(labelAnd);
        add(upperField);
        add(Box.createHorizontalStrut(100));
        add(removeButton);
    }

    private void enableUpper(boolean enable) {
        labelAnd.setEnabled(enable);
        upperField.setEnabled(enable);
    }

    public void setComp(Comp Comp) {
        typeCombo.setSelectedIndex(Comp.type);
        relationCombo.setSelectedIndex(Comp.relation);
        lowerField.setText(Comp.lower);
        upperField.setText(Comp.upper);
        enableUpper(Comp.relation == 2);
    }

    public Comp getComp() {
        return new Comp(typeCombo.getSelectedIndex(), relationCombo.getSelectedIndex(), lowerField.getText(), upperField.getText());
    }
}

2
从模型外部调用fireXX返回-1,不尊重Java命名约定返回-1...你可以做得更好 :-) - kleopatra
还不满意 - fireDataChanged不是必要的:默认行为足够好以触发rowsInserted :) - kleopatra
@kleopatra同意了这一点,还删除了我其他的“添加”。 - mKorbel
@kleopatra,非常好的发现,但为了避免玩弄无用的值,需要使用JTable#fireTableRowsInserted(int firstRow, int lastRow)。然后我禁用了JTable#fireTableDataChanged(),不用担心,这是默认的TableModel :-) - mKorbel
+1 一个不错的例子。我想你指的是“所有_method名称_都以小写字母开头”。此外,我想知道Comp是否可以用enum,一个是type,另一个是relation - trashgod

4

3
将所有搜索条件的组件添加到面板中,并添加/删除特定的面板。我认为在这里使用TableModel不是一个好选择。

你能以某种方式证明一下你的观点吗?它只是看起来过于复杂了吗,还是其他原因? - Rolf
虽然这肯定是个品味问题,但我倾向于同意 - @Rolf:我在要求中并没有看到太多“表格性”。mKorbel的代码示例基本上是一个缩小的(单列)表格,由于JList不支持编辑,它不是列表的唯一原因。 - kleopatra
我同意GUI不是特别表格化。然而,就像你所说的,JList不支持编辑,那么使用一个表格有什么问题,即使它伪装成一个列表?实际上,我更倾向于使用一个四列表格,其中组合选择器、否定、条件和删除按钮都有自己的列。 - Rolf
更重要的是,您有没有一种优雅的方法来实现这个而不使用表格? - Rolf

0

Netbeans有一个漂亮的UI,与您描述的类似: Task List Filter KeywordPanel

为什么不站在巨人的肩膀上呢? Netbeans面板看起来很好,功能也很好。 实现甚至在UI和模型代码之间清晰地分离。 如果我身处您的位置(并且是2011年6月),我会基于此处的源代码构建我的解决方案:

http://source.apidesign.org/hg/netbeans/releases/file/cb41fb91bc28/tasklist.ui/src/org/netbeans/modules/tasklist/filter

KeywordPanel.java中包含这样的注释:“GUI基于Mozilla邮件工具的界面设计”。

抱歉回复晚了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接