如何复制JButton操作并更改其对底层对象的引用



以下示例使用JButton、JTextField和JLabel创建JFrame。当按下按钮时,它会增加文本字段和标签中的值。这个例子还创建了一个第二个JFrame,它是第一个的副本。按钮、文本字段和标签也会被复制。

手头的问题是,复制帧上的按钮仍然会更新原始帧上的文本字段和标签。"为什么"是相当明显的,因为代码对文本字段和标签进行了特定的引用。

虽然这不是以最好的方式写的,但它是我所处理的场景的一个很好的例子。

目标是,在没有重大重写的情况下,让复制的按钮操作更新复制的测试字段和标签而不是原始字段和标签的侵入性最小的方法是什么?

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class ButtonTextFieldLabel extends JFrame
{
JButton bnt1 = new JButton("B1");
JTextField tf1 = new JTextField("1");
JLabel lbl1 = new JLabel("100");
public ButtonTextFieldLabel()
{
super("Main Frame");
setLayout(null);
bnt1.setBounds(50,100,120,40);
tf1.setBounds(300,100, 80,40);
lbl1.setBounds(200,100,80,40);
bnt1.addActionListener(new ListenerHolder(this));
add(bnt1);
add(tf1);
add(lbl1);
setSize(500,500);
makeCopy(this);       
setVisible(true);
}
private void makeCopy(ButtonTextFieldLabel originalObj) 
{
JFrame copyFrame = new JFrame();
copyFrame.setTitle("Copy of " + originalObj.getTitle());
copyFrame.setSize(originalObj.getSize());
copyFrame.setLocation(originalObj.getX()+100, originalObj.getY()+100);
copyFrame.setLayout(null);
JButton copyBnt1 = new JButton();       
copyBnt1.setBounds(originalObj.bnt1.getBounds());
copyBnt1.setLabel(originalObj.bnt1.getLabel());
copyFrame.add(copyBnt1);
for (ActionListener al : originalObj.bnt1.getActionListeners())
{
copyBnt1.addActionListener(al);
}
JTextField copyTf1 = new JTextField();
copyTf1.setBounds(originalObj.tf1.getBounds());
copyTf1.setText(originalObj.tf1.getText());
JLabel copyLbl1 = new JLabel();
copyLbl1.setBounds(originalObj.lbl1.getBounds());
copyLbl1.setText(originalObj.lbl1.getText());
copyFrame.add(copyBnt1); 
copyFrame.add(copyTf1);
copyFrame.add(copyLbl1);
copyFrame.setVisible(true);
}
public void runThis() 
{
tf1.setText(  Integer.toString(Integer.parseInt(tf1.getText())+1)   );
lbl1.setText(  Integer.toString(Integer.parseInt(lbl1.getText())+1)   );
}
} 
class ListenerHolder implements  ActionListener
{
ButtonTextFieldLabel ph;
public ListenerHolder(ButtonTextFieldLabel ph)
{
this.ph = ph;       
}
@Override
public void actionPerformed(ActionEvent arg0) 
{
ph.runThis();
}
}
public class TestBTL
{
public static void main(String[] args){
new ButtonTextFieldLabel();
}
}

您已经知道问题的原因——您正在复制原始ActionListener,并完成对原始GUI组件的引用。总体解决方案不是复制动作侦听器,而是创建GUI以保持和维护它们自己的唯一状态。一种解决方案是,创建一个独立的GUI对象来保存和更新自己的状态,而不是试图通过拼凑来复制组件。如果需要,可以使用工厂方法创建多个GUI。

例如:

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class TestBtl2 {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndDisplayFrame("Frame 1").setVisible(true);
createAndDisplayFrame("Frame 2").setVisible(true);
});
}
// Factory method
private static JFrame createAndDisplayFrame(String text) {
BtlPanel btlPanel = new BtlPanel();
JFrame frame = new JFrame(text);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(btlPanel);
frame.pack();
frame.setLocationByPlatform(true);
return frame;
}
}
class BtlPanel extends JPanel {
private int value = 0;
private JButton button1 = new JButton(new ButtonAction("Button 1"));
private JLabel label1 = new JLabel("00");
private JTextField textField1 = new JTextField("00");
public BtlPanel() {
textField1.setFocusable(false);
add(button1);
add(Box.createHorizontalStrut(20));
add(label1);
add(Box.createHorizontalStrut(20));
add(textField1);
setPreferredSize(new Dimension(300, 100));
}
public void incrementValue() {
value++;
String text = String.format("%02d", value);
label1.setText(text);
textField1.setText(text);
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name) {
super(name);
}
@Override
public void actionPerformed(ActionEvent e) {
incrementValue();
}
}
}

侧面建议:

  • 虽然在Swing新手看来,null布局和setBounds()可能是创建复杂GUI的最简单、最好的方法,但您创建的Swing GUI越多,在使用它们时就会遇到更严重的困难。当GUI调整大小时,它们不会调整组件的大小,它们是一个需要增强或维护的皇家女巫,当放在滚动窗格中时,它们会完全失败,当在所有平台上查看时,或者当屏幕分辨率与原始分辨率不同时,它们看起来非常糟糕
  • 查看:多个JFrame的使用,好的/坏的做法

最新更新