【问题标题】:Refresh JPanel content on tab switch在选项卡开关上刷新 JPanel 内容
【发布时间】:2013-05-07 19:50:14
【问题描述】:

我正在编写一个简单的 UI,只是为了掌握事情的窍门。我有一个带有两个选项卡的选项卡式窗口,一个有一个计数整数的按钮,另一个有一个显示所述整数内容的文本字段。或者至少这是计划。

如果我把所有东西都塞进一个班级,一切都会很好。我可以从我的 actionlistener 访问选项卡 1,并通过在选项卡 2 中按下按钮来更改选项卡 1 中的文本字段。但显然我不希望我的整个程序属于一个类。

在这里我不知道该怎么做:我需要告诉 Class Tab1 中的文本字段在 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()) 的方法,或者类似的东西?如果是这样,我该怎么做?

【问题讨论】:

  • 你有没有想过使用GettersSetters

标签: java swing jpanel jtabbedpane


【解决方案1】:

以这种方式分离控件后,您可以选择视图...

你可以...

与其他每个选项卡共享每个“选项卡”的实例,允许它们访问其他控件或相互附加侦听器。这是非常紧密耦合和混乱的。

另一个问题是,按钮真的关心文本字段还是反之亦然...

你可以...

创建一个包含当前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&lt;N extends Number&gt; 之类的操作来成为一个稍微灵活一点的模型,这将允许您定义可以容纳IntegerDoubleFloatLong 的模型,但我会离开给你。

每个选项卡视图都需要一个setModel(NumberModel) 方法,因此您可以将模型传递给它。在这些方法中,您将附加一个监听器到模型和get 当前值,以便模型和视图同步。

【讨论】:

  • 嗯,我如何触发 ChangeEvent(或任何其他类型的事件)以使其被侦听器捕获?
  • 首先,您的模型需要一个侦听器列表(例如 ChangeListener)。当值改变时,你遍历列表并调用 stateChanged 方法
  • 谢谢!这帮助很大。
猜你喜欢
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 2021-11-20
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多