在制表符开关上刷新JPanel内容

时间:2013-05-07 19:50:15

标签: java swing jpanel jtabbedpane

我正在编写一个简单的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>

1 个答案:

答案 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>之类的操作,我们可以成为一个更灵活的模型,这样您就可以定义可以保留IntegerDoubleFloat和{{1}的模型例如,但我会把它留给你。

每个标签视图都需要Long方法,因此您可以传递模型。在这些方法中,您将为模型附加一个侦听器,并将setModel(NumberModel)附加到当前值,以便模型和视图保持同步。