从 JFrame 中动态创建的变量获取数据
Getting data from dynamically created variables in JFrame
我有一个包含 Jspinner 的 Jframe。当 JSpinner 增加时,会在我的面板 headerPanel 中创建一个新的 JTextField。该脚本创建文本框,并在其变量名称(tFrame0、tFrame1 等)上附加一个整数。
原始代码和所有旧编辑已移至 This pastebin。
链接 pastebin 以防它在将来对任何人有帮助,并且不要弄乱当前相关的代码。
在 TreffnonX 的帮助下解决了这个问题!感谢您在评论和聊天中对我非常耐心。
这是工作代码,以防以后有人遇到此类问题。
全局变量
private JPanel headerPanel;
private JSpinner spinner;
public List<JTextField> findTextFields() {
List<JTextField> fields = new LinkedList<>();
Component[] children = headerPanel.getComponents();
for (Component child : children) {
if (child instanceof JTextField) {
JTextField childField = (JTextField) child;
// check, if the name is prefixed correctly.
String name = childField.getName();
if (name.startsWith(nameTField)) {
fields.add(childField);
}
}
}
return fields;
}
JSpinner
spinner = new JSpinner();
spinner.setModel(new SpinnerNumberModel(1, 1, 100, 1));
spinner.addContainerListener(new ContainerAdapter() {
@Override
public void componentAdded(ContainerEvent arg0) {
adaptBoxes();
}
});
spinner.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
int spinnerValue = (Integer) spinner.getValue();
if (spinnerValue == headerPanel.getComponentCount()) {
System.out.println("Error, spinner shouldn't change to same alue");
}
adaptBoxes();
frame.revalidate();
frame.repaint();
}
});
adaptBoxes();
自适应框();方法
public void adaptBoxes() {
// Find value of spinner.
int spinnerValue = (Integer) spinner.getValue();
List<JTextField> textFields = findTextFields();
int numTextFields = textFields.size();
if (numTextFields > spinnerValue) {
// if we have too many fields.
for (JTextField textField : textFields) {
String name = textField.getName();
Matcher matcher = POSTFIX_PATTERN.matcher(name);
if (matcher.matches()) {
String strPostfix = matcher.group(1);
int postFixNumeric = Integer.parseInt(strPostfix);
System.out.println("for postFix = " + postFixNumeric + ": " + textField.getText());
if (postFixNumeric >= spinnerValue) {
System.out.println("PFN: " + postFixNumeric);
System.out.println("FTF: " + numTextFields);
headerPanel.remove(textField);
}
}
}
} else {
while (numTextFields < spinnerValue) {
// if we have too few fields.
int hp = headerPanel.getComponentCount();
JTextField tField = new JTextField();
tField.setName(nameTField + hp);
tField.getDocument().addDocumentListener(new TextFieldDocumentListener());
headerPanel.add(tField);
textFields = findTextFields();
numTextFields = textFields.size();
}
}
}
JTextField 文档监听器
/**
* Inner class
*/
private final class TextFieldDocumentListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent e) {
warn();
}
@Override
public void removeUpdate(DocumentEvent e) {
warn();
}
@Override
public void insertUpdate(DocumentEvent e) {
warn();
}
/**
* Actual sysout to inform the user...
*/
public void warn() {
List<JTextField> textFields = findTextFields();
for (JTextField textField : textFields) {
String name = textField.getName();
Matcher matcher = POSTFIX_PATTERN.matcher(name);
System.out.println(name);
if (matcher.matches()) {
String strPostfix = matcher.group(1);
int postfixNumeric = Integer.parseInt(strPostfix);
System.out.println("for postfix = " + postfixNumeric + ": " + textField.getText());
} else {
System.out.println("matcher error: " + matcher);
}
}
}
}
你不能 'append my for loop int i to a variable name',除非你想使用反射 (https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/package-summary.html)。反射将允许您搜索 java-Object
的字段,并根据它们的名称(如果公开)访问它们。
幸运的是,这不是您真正需要的。您实际上正在寻找一种方法来按类型和名称识别 J?Panel 的子项 (Component
s),然后推断出您以前附加的索引。我的建议是执行以下操作:
// This pattern can be anywhere in the Class-file:
public static final Pattern POSTFIX_PATTERN = Pattern.compile("tFrame(\d+)");
// This should be in your exposed GUI class (supposedly the Panel), but can be anywhere it can access headerPanel.
public List<JTextField> findTextFields() {
List<JTextField> fields = new LinkedList<>();
Component[] children = headerPanel.getComponents();
// this is your 'search-loop':
for (Component child : children) {
if (child instanceof JTextField) {
JTextField childField = (JTextField) child;
String name = childField.getName();
if (name.startsWith("tFrame")) {
fields.add(childField);
}
}
}
return fields;
}
(这也可以像Stream
一样完成,但上面的代码更简单)。
上面的代码 'finds' 您现有的组件,您可以从那里开始工作,例如根据它们的名称对它们进行排序,然后进行打印。
您只收到部分文本字段似乎对修改有反应的问题是因为您(可能)没有将相同的 inputData
-array 传递给不同的 DocumentListener
s你创造。如果您设置断点并检查,我敢打赌 inputData-Arrays 具有不同的哈希值。也就是说,我不能 100% 推断出这个,因为周围的代码不完整。
我建议更改:
if (tField.getText() != null) {
Object[] currentData = new Object[getFieldNum + 1];
currentData[getFieldNum] = tField.getText();
inputData[getFieldNum] = tField.getText();
for (int i = 0; i < inputData.length; i++) {
if (inputData[i] != null) {
System.out.println("for i = " + i + ": " + inputData[i]);
}
}
}
至
for (JTextField textField : findTextFields()) {
String name = textField.getName();
Matcher matcher = POSTFIX_PATTERN.matcher(name);
if (matcher.matches()) {
String strPostfix = matcher.group(1);
int postfixNumeric = Integer.parseInt(strPostfix);
System.out.println("for postfix = " + postfixNumeric + ": " + textField.getText());
}
}
这样,您就可以在用户实际做出您想要响应的更改的时间点获取组件。
另外附注:
一旦您减少或增加 JSpinner 上的数字,您真的想要替换所有现有的文本字段及其当前状态吗?
可以考虑去掉postfixNumeric >= getTextFields().size()
的元素,或者增加元素的个数而不去掉JSpinner上小于前一个个数的元素。这可以用你得到的循环来完成。
编辑
问题原来是由于使用了大量 instance-fields 而不是局部变量。由于只发布了部分代码,因此很难甚至不可能仅从问题中确定。
我有一个包含 Jspinner 的 Jframe。当 JSpinner 增加时,会在我的面板 headerPanel 中创建一个新的 JTextField。该脚本创建文本框,并在其变量名称(tFrame0、tFrame1 等)上附加一个整数。
原始代码和所有旧编辑已移至 This pastebin。
链接 pastebin 以防它在将来对任何人有帮助,并且不要弄乱当前相关的代码。
在 TreffnonX 的帮助下解决了这个问题!感谢您在评论和聊天中对我非常耐心。
这是工作代码,以防以后有人遇到此类问题。
全局变量
private JPanel headerPanel;
private JSpinner spinner;
public List<JTextField> findTextFields() {
List<JTextField> fields = new LinkedList<>();
Component[] children = headerPanel.getComponents();
for (Component child : children) {
if (child instanceof JTextField) {
JTextField childField = (JTextField) child;
// check, if the name is prefixed correctly.
String name = childField.getName();
if (name.startsWith(nameTField)) {
fields.add(childField);
}
}
}
return fields;
}
JSpinner
spinner = new JSpinner();
spinner.setModel(new SpinnerNumberModel(1, 1, 100, 1));
spinner.addContainerListener(new ContainerAdapter() {
@Override
public void componentAdded(ContainerEvent arg0) {
adaptBoxes();
}
});
spinner.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
int spinnerValue = (Integer) spinner.getValue();
if (spinnerValue == headerPanel.getComponentCount()) {
System.out.println("Error, spinner shouldn't change to same alue");
}
adaptBoxes();
frame.revalidate();
frame.repaint();
}
});
adaptBoxes();
自适应框();方法
public void adaptBoxes() {
// Find value of spinner.
int spinnerValue = (Integer) spinner.getValue();
List<JTextField> textFields = findTextFields();
int numTextFields = textFields.size();
if (numTextFields > spinnerValue) {
// if we have too many fields.
for (JTextField textField : textFields) {
String name = textField.getName();
Matcher matcher = POSTFIX_PATTERN.matcher(name);
if (matcher.matches()) {
String strPostfix = matcher.group(1);
int postFixNumeric = Integer.parseInt(strPostfix);
System.out.println("for postFix = " + postFixNumeric + ": " + textField.getText());
if (postFixNumeric >= spinnerValue) {
System.out.println("PFN: " + postFixNumeric);
System.out.println("FTF: " + numTextFields);
headerPanel.remove(textField);
}
}
}
} else {
while (numTextFields < spinnerValue) {
// if we have too few fields.
int hp = headerPanel.getComponentCount();
JTextField tField = new JTextField();
tField.setName(nameTField + hp);
tField.getDocument().addDocumentListener(new TextFieldDocumentListener());
headerPanel.add(tField);
textFields = findTextFields();
numTextFields = textFields.size();
}
}
}
JTextField 文档监听器
/**
* Inner class
*/
private final class TextFieldDocumentListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent e) {
warn();
}
@Override
public void removeUpdate(DocumentEvent e) {
warn();
}
@Override
public void insertUpdate(DocumentEvent e) {
warn();
}
/**
* Actual sysout to inform the user...
*/
public void warn() {
List<JTextField> textFields = findTextFields();
for (JTextField textField : textFields) {
String name = textField.getName();
Matcher matcher = POSTFIX_PATTERN.matcher(name);
System.out.println(name);
if (matcher.matches()) {
String strPostfix = matcher.group(1);
int postfixNumeric = Integer.parseInt(strPostfix);
System.out.println("for postfix = " + postfixNumeric + ": " + textField.getText());
} else {
System.out.println("matcher error: " + matcher);
}
}
}
}
你不能 'append my for loop int i to a variable name',除非你想使用反射 (https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/package-summary.html)。反射将允许您搜索 java-Object
的字段,并根据它们的名称(如果公开)访问它们。
幸运的是,这不是您真正需要的。您实际上正在寻找一种方法来按类型和名称识别 J?Panel 的子项 (Component
s),然后推断出您以前附加的索引。我的建议是执行以下操作:
// This pattern can be anywhere in the Class-file:
public static final Pattern POSTFIX_PATTERN = Pattern.compile("tFrame(\d+)");
// This should be in your exposed GUI class (supposedly the Panel), but can be anywhere it can access headerPanel.
public List<JTextField> findTextFields() {
List<JTextField> fields = new LinkedList<>();
Component[] children = headerPanel.getComponents();
// this is your 'search-loop':
for (Component child : children) {
if (child instanceof JTextField) {
JTextField childField = (JTextField) child;
String name = childField.getName();
if (name.startsWith("tFrame")) {
fields.add(childField);
}
}
}
return fields;
}
(这也可以像Stream
一样完成,但上面的代码更简单)。
上面的代码 'finds' 您现有的组件,您可以从那里开始工作,例如根据它们的名称对它们进行排序,然后进行打印。
您只收到部分文本字段似乎对修改有反应的问题是因为您(可能)没有将相同的 inputData
-array 传递给不同的 DocumentListener
s你创造。如果您设置断点并检查,我敢打赌 inputData-Arrays 具有不同的哈希值。也就是说,我不能 100% 推断出这个,因为周围的代码不完整。
我建议更改:
if (tField.getText() != null) {
Object[] currentData = new Object[getFieldNum + 1];
currentData[getFieldNum] = tField.getText();
inputData[getFieldNum] = tField.getText();
for (int i = 0; i < inputData.length; i++) {
if (inputData[i] != null) {
System.out.println("for i = " + i + ": " + inputData[i]);
}
}
}
至
for (JTextField textField : findTextFields()) {
String name = textField.getName();
Matcher matcher = POSTFIX_PATTERN.matcher(name);
if (matcher.matches()) {
String strPostfix = matcher.group(1);
int postfixNumeric = Integer.parseInt(strPostfix);
System.out.println("for postfix = " + postfixNumeric + ": " + textField.getText());
}
}
这样,您就可以在用户实际做出您想要响应的更改的时间点获取组件。
另外附注: 一旦您减少或增加 JSpinner 上的数字,您真的想要替换所有现有的文本字段及其当前状态吗?
可以考虑去掉postfixNumeric >= getTextFields().size()
的元素,或者增加元素的个数而不去掉JSpinner上小于前一个个数的元素。这可以用你得到的循环来完成。
编辑
问题原来是由于使用了大量 instance-fields 而不是局部变量。由于只发布了部分代码,因此很难甚至不可能仅从问题中确定。