如何在 Swing 中实现动态 GUI
Posted
技术标签:
【中文标题】如何在 Swing 中实现动态 GUI【英文标题】:How to implement dynamic GUI in swing 【发布时间】:2011-09-15 09:08:02 【问题描述】:首先,很抱歉发布了一些可能过于具体的内容,但我对 Swing 的经验不是很丰富,似乎找不到适合我需要的好例子。
所以我试图找出实现动态 GUI 以在 Swing 中选择过滤条件的最佳方法:
基础模型是一个类,其中包含可以否定的条件列表(即应用 NOT 前缀),以及指示这些条件是否应与 AND 或 OR 组合的属性。
GUI 将允许用户添加、更改或删除标准,并选择组合运算符(和/或)。第一个标准自然不会有组合选择器,第三个和后续标准将简单地使用与第二个相同的组合操作符。
右侧的 X 按钮将用于删除标准。当按下添加按钮时,会在底部添加一行新的组件。 随着更改的进行,这些将反映在基础模型中。
当然,我可以通过简单地将组件添加到 JPanel,然后相应地更新模型来实现这一点,但我更喜欢更简洁的解决方案,例如 TableModel 提供的解决方案。
所以我想知道具有自定义 TableModel 和 TableCellRenderer/Editor 的表是否是最好的方法,或者是否有更好的方法来实现类似的东西。如果 table 确实是最好的方法,我会很感激一些关于如何使用 TableCellRenderers 或 -Editors 来实现这一点的指针。
提前致谢。
【问题讨论】:
供参考,“criteria”的单数是"criterion"。另见criterium,一种自行车比赛。 哎呀,不是我的母语 :) 【参考方案1】:仅作为示例,为了便于理解,所有内容都是硬编码的
编辑:
正如 kleopatra 所注意到的,将 JTable#fireTableDataChanged() 从 ActionListener 移至 TableModel,修改了所有以小写开头的 ClassName
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());
【讨论】:
-1 用于从模型外部调用 fireXX,-1 用于不尊重 java 命名约定......来吧,你可以做得更好:-) 仍然不满意 - fireDataChanged 不是必需的:默认行为足以触发 rowsInserted :) @kleopatra 同意这一点,并删除了我的其他“添加” @kleopatra o.k 真的很棒,但是为了避免使用 JTable#fireTableRowsInserted(int firstRow, int lastRow); 所需的无用值;然后我禁用了 JTable#fireTableDataChanged(),不用担心这是 DefalutTableModel :-) +1 好例子。我认为您的意思是“所有方法名称都以小写开头。”另外,我想知道Comp
是否可以是enum
,一个是type
,一个是relation
。【参考方案2】:
我认为这样的自定义 TableMOdel 和 TableCellRenderer/Editor 是最好的选择。 http://download.oracle.com/javase/tutorial/uiswing/components/table.html 这将是一个很好的开始。
【讨论】:
【参考方案3】:将搜索条件的所有组件添加到面板并添加/删除特定面板。我认为 Tablemodel 在这里不是一个好的选择。
【讨论】:
你能以某种方式证明这种观点吗?是不是看起来有点矫枉过正,还是什么? 虽然肯定是口味问题,但我倾向于同意 - @Rolf:我在要求中看不到太多“表格性”。 mKorbel 的代码示例基本上是一个缩小的(单列)表,它不是列表的唯一原因是 JList 不支持编辑:-) 我同意 GUI 不是特别表格的。但是,就像你说的,JList 不支持编辑,那么使用表格有什么问题,即使它冒充列表?实际上,我更多地考虑的是四列表格,其中组合选择器、否定、标准和删除按钮将有自己的列。 更重要的是,您有没有建议以一种优雅 的方式在不使用表格的情况下实现这一点?【参考方案4】:Netbeans 有一个不错的用户界面,它的功能类似于您所描述的:
为什么不站在巨人的肩膀上呢? Netbeans 面板看起来不错并且运行良好。实现甚至在 ui 和模型代码之间完全分离。如果我站在你的立场上(那是 2011 年 6 月),我的解决方案将基于此处的来源:
http://hg.netbeans.org/main/file/14d339767aef/tasklist.ui/src/org/netbeans/modules/tasklist/filter
KeywordPanel.java 包含这个神秘的注释:“GUI 基于 Mozilla 邮件工具中的那个”。
I wonder what that could be?
抱歉回复晚了。
【讨论】:
以上是关于如何在 Swing 中实现动态 GUI的主要内容,如果未能解决你的问题,请参考以下文章
用java编写的swing程序。如何在主界面中实现刷新的功能,就是让主界面的组件刷新
用java编写的swing程序。如何在主界面中实现刷新的功能,就是让主界面的组件刷新