我正在编写一个简单的UI,只是为了解决问题。我有一个带有两个选项卡的选项卡式窗口,一个有一个按钮,它计算一个整数,另一个有一个显示所述整数内容的文本字段。或者至少那是计划。
如果我把所有东西都塞进一个班级,那么一切都很好。我可以从我的actionlistener访问选项卡1,并从选项卡2中的按钮按下更改选项卡1中的文本字段。但是我不希望我的整个程序显然属于一个类。
在这里我不知道该怎么做:我需要告诉Class Tab1中的textfield在Class Tab2中按下按钮。这里做什么是正确的?我的第一个想法是在Tab2的创建中移交Tab1的实例,所以我可以做tab1.changeText()。但是,一旦我获得了更多相互交互的标签,那就会很快变得混乱。所以,相反,我想在每次打开时更新第一个标签的内容,但我不知道该怎么做。我也不知道这是否正确。所以,帮助!
这是一些代码。 “content”是Content的一个实例,一个处理所有逻辑的类,如添加到计数器。
主GUI类:
public class GUI extends JFrame {
//Stuff..
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("One", new Tab1(content));
tabs.addTab("Two", new Tab2(content));
//Stuff..
标签1:
public class Tab1 extends JPanel {
public Tab1(Content content) {
JPanel tab1 = new JPanel();
//Stuff..
JTextField tfCount = new JTextField(content.getCounter(), 10);
tab1.add(tfCount);
this.add(tab1);
//Stuff..
标签2:
public class Tab2 extends JPanel {
public Tab2(Content content) {
JPanel tab2 = new JPanel();
//Stuff..
JButton btnCount2 = new JButton("Count");
btnCount2.addActionListener(new TestListener(this.content));
tab2.add(btnCount2);
this.add(tab2);
}
private class TestListener implements ActionListener {
Content content;
public TestListener(Content content) {
this.content = content;
}
@Override
public void actionPerformed(ActionEvent e) {
this.content.addToCounter(1);
}
}
现在,如果所有这些都在一个类(加上子类)中,我可以从Tab2访问tfCount并执行tfCount.setText(content.getCounter());.现在tfCount在不同的类中,我无法访问它,除非我将Tab1的实例移交给Tab2(如tabs.addTab(“Two”,new Tab2(content,Tab1);)。我不能而不是每次打开时让Tab1重新绘制自己,就像有一个方法在Tab1中打开时执行tfCount.setText(content.getCounter()),或者沿着那些行执行某些操作?如果是这样,我该怎么做?< / p>
答案 0 :(得分:2)
如果您以这种方式分开控件,则可以选择视图...
与每个其他选项卡共享每个“选项卡”的实例,允许它们访问其他控件或将侦听器彼此连接。这是紧密耦合和混乱。
另一个问题是,按钮是否真的关心文本字段,反之亦然......
创建一个包含当前int
值的简单模型,并提供更改该值的方法。
当值发生变化时,该模型将具有触发ChangeEvent
(例如)的能力,感兴趣的各方可以相应地监听并更新自己。
这解耦了代码,降低了复杂性,并大大提高了代码中各种元素的灵活性和重用性。
这通常称为观察者模式,广泛用于Swing。
对我来说,我总是从界面开始,这描述了为达到所需目标必须满足的绝对最低要求。每个选项卡都想知道当前值,能够设置下一个值和监听器以更改模型......
public interface NumberModel {
public int getValue();
public void setValue(int value);
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
abstract
实现处理更“常见”的实现细节,具体实现不希望实现的内容,因为它对所有实现都是通用的。在这种情况下,那将是听众管理......
public abstract class AbstractNumberModel implements NumberModel {
private List<ChangeListener> listeners;
public AbstractNumberModel() {
listeners = new ArrayList<>(25);
}
@Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected ChangeListener[] getChangeListeners() {
// FIFO...
List<ChangeListener> copy = new ArrayList<>(listeners);
Collections.reverse(copy);
return copy.toArray(copy.toArray(new ChangeListener[listeners.size()]));
}
protected void fireStateChanged() {
ChangeListener[] listeners = getChangeListeners();
if (listeners != null && listeners.length > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
}
最后,一个具体的实现,它处理实现特定的细节......
public class DefaultNumberModel extends AbstractNumberModel {
private int value;
public DefaultNumberModel() {
}
public DefaultNumberModel(int value) {
setValue(value);
}
@Override
public int getValue() {
return value;
}
@Override
public void setValue(int num) {
if (num != value) {
value = num;
fireStateChanged();
}
}
}
通过public interface NumberModel<N extends Number>
之类的操作,我们可以成为一个更灵活的模型,这样您就可以定义可以保留Integer
,Double
,Float
和{{1}的模型例如,但我会把它留给你。
每个标签视图都需要Long
方法,因此您可以传递模型。在这些方法中,您将为模型附加一个侦听器,并将setModel(NumberModel)
附加到当前值,以便模型和视图保持同步。