Обновить содержимое JPanel при переключении вкладок

Я пишу простой пользовательский интерфейс, чтобы разобраться. У меня есть окно с вкладками с двумя вкладками, в одной есть кнопка, которая подсчитывает целое число, а в другой есть текстовое поле, показывающее содержимое указанного целого числа. По крайней мере, таков план.

Все работает просто отлично, если я запихну все в один класс. Я могу получить доступ к вкладке 1 из моего списка действий и изменить текстовое поле на вкладке 1 нажатием кнопки на вкладке 2. Но я, очевидно, не хочу, чтобы вся моя программа находилась в одном классе.

И здесь я понятия не имею, что делать: мне нужно сказать, чтобы текстовое поле в Class Tab1 менялось при нажатии кнопки в Class Tab2. Что тут делать правильно? Моей первой мыслью было передать экземпляр Tab1 при создании Tab2, чтобы я мог выполнить tab1.changeText(). Но это быстро станет беспорядочным, как только я получу больше вкладок, которые взаимодействуют друг с другом. Итак, вместо этого я хочу обновлять содержимое первой вкладки каждый раз, когда она открывается, но я не знаю, как это сделать. И я тоже не знаю, правильно ли это. Итак, помогите!

Вот код. «content» — это экземпляр Content, класс, обрабатывающий всю логику, например добавление к счетчику.

Основной класс графического интерфейса:

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);
    }
}

Теперь, если бы все это было в одном классе (плюс подклассы), я мог бы просто получить доступ к tfCount из Tab2 и выполнить tfCount.setText(content.getCounter());. Однако теперь tfCount находится в другом классе, и я не могу получить к нему доступ, если не передам экземпляр Tab1 в Tab2 (например, tabs.addTab("Two", new Tab2(content, Tab1);). Не мог бы я вместо этого заставьте Tab1 перекрашивать себя всякий раз, когда он открывается, например, иметь метод, который выполняет tfCount.setText(content.getCounter()) в Tab1 всякий раз, когда он открывается, или что-то в этом роде?Если да, то как мне это сделать?


person Conti    schedule 07.05.2013    source источник
comment
Вы думали об использовании Getters и Setters?   -  person PM 77-1    schedule 07.05.2013


Ответы (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<N extends Number>, что позволило бы вам определить модели, которые могли бы содержать, например, Integer, Double, Float и Long, но я оставлю это вам.

Для каждого из ваших представлений вкладок потребуется метод setModel(NumberModel), поэтому вы можете передать его модели. В этих методах вы прикрепите прослушиватель к модели и get текущее значение, чтобы модель и представление были синхронизированы.

person MadProgrammer    schedule 07.05.2013
comment
Хм, как запустить ChangeEvent (или любое другое событие), чтобы его перехватил прослушиватель? - person Conti; 08.05.2013
comment
Во-первых, вашей модели нужен список слушателей (скажем, ChangeListener). Когда значение изменяется, вы проходите по списку и вызываете метод stateChanged. - person MadProgrammer; 08.05.2013
comment
Спасибо! Это очень помогло. - person Conti; 08.05.2013