Запретить смену вкладок в JTabbedPane

Я пытаюсь запретить пользователю менять вкладку, когда текущая вкладка недействительна. Поэтому, когда он нажимает на вкладку, я хочу проверить, является ли текущая «действительной», а если нет, остаться на текущей вкладке. Я пытался использовать VetoableChangeListener, который не работал, код никогда не входит в метод vetoableChange:

jTabbedPane.addVetoableChangeListener(new VetoableChangeListener() {

  @Override
  public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
    if (!isCurrentTabValid()) {
      throw new PropertyVetoException("test", evt);
    }
  }
});

Как я могу сделать это правильно?

Спасибо!


person Nanocom    schedule 12.09.2012    source источник


Ответы (3)


VetoableChangeListener полезен только в том случае, если класс, в котором он зарегистрирован, инициирует наложение вето на изменение свойства. Большинство (все? Ни разу не встречал) свойства JComponents и подклассов не подлежат вето. Кроме того, выбор обрабатывается SingleSelectionModel, а не самим компонентом.

Эта модель является крюком для поддержки изменений, на которые можно наложить вето.

  • реализовать пользовательскую модель, которая запускает vetoablePropertyChange при изменении выбора
  • если ни один из его слушателей не возражает, внесите изменения, в противном случае ничего не делайте
  • установите пользовательскую модель на tabbedPane
  • реализовать VetoablePropertyChangeListener, который содержит логику проверки
  • зарегистрировать vetoableListener в модели

в коде что-то вроде

public static class VetoableSingleSelectionModel extends
        DefaultSingleSelectionModel {

    private VetoableChangeSupport vetoableChangeSupport;

    @Override
    public void setSelectedIndex(int index) {
        if (getSelectedIndex() == index)
            return;
        try {
            fireVetoableChange(getSelectedIndex(), index);
        } catch (PropertyVetoException e) {
            return;
        }
        super.setSelectedIndex(index);
    }

    private void fireVetoableChange(int oldSelectionIndex,
            int newSelectionIndex) throws PropertyVetoException {
        if (!isVetoable())
            return;
        vetoableChangeSupport.fireVetoableChange("selectedIndex",
                oldSelectionIndex, newSelectionIndex);

    }

    private boolean isVetoable() {
        if (vetoableChangeSupport == null)
            return false;
        return vetoableChangeSupport.hasListeners(null);
    }

    public void addVetoableChangeListener(VetoableChangeListener l) {
        if (vetoableChangeSupport == null) {
            vetoableChangeSupport = new VetoableChangeSupport(this);
        }
        vetoableChangeSupport.addVetoableChangeListener(l);
    }

    public void removeVetoableChangeListener(VetoableChangeListener l) {
        if (vetoableChangeSupport == null)
            return;
        vetoableChangeSupport.removeVetoableChangeListener(l);
    }

}

// usage
JTabbedPane pane = new JTabbedPane();
VetoableSingleSelectionModel model = new VetoableSingleSelectionModel();
VetoableChangeListener validator = new VetoableChangeListener() {

    @Override
    public void vetoableChange(PropertyChangeEvent evt)
            throws PropertyVetoException {
        int oldSelection = (int) evt.getOldValue();
        if ((oldSelection == -1) || isValidTab(oldSelection)) return;

        throw new PropertyVetoException("change not valid", evt);

    }

    private boolean isValidTab(int oldSelection) {
        // implement your validation logic here
        return false;
    }
};
model.addVetoableChangeListener(validator);
pane.setModel(model);
pane.addTab("one", new JLabel("here we are and stay"));
pane.addTab("other", new JLabel("poor me, never shown"));
person kleopatra    schedule 12.09.2012
comment
Это отлично работает для смены вкладок, но как наложить вето на закрытие вкладок? vetoableChange вызывается после закрытия вкладки и при выборе предыдущей вкладки. Я искал в Интернете vetoableClose, но не нашел. - person Alex Burdusel; 28.02.2014
comment
звучит несвязанно / расширено - пожалуйста, задайте вопрос с SSCCE - person kleopatra; 01.03.2014
comment
Все в порядке, я решил это. У меня есть компонент кнопки, добавленный на каждой вкладке, чтобы закрыть их. Он вызывает JTabbedPane.remove(tabIndex). Мне удалось обойти это. Я выбираю предыдущую вкладку перед закрытием текущей, поэтому вызывается метод valueChanged. - person Alex Burdusel; 01.03.2014

Похоже, вы хотите сначала отключить вкладку. Затем, когда текущая страница действительна, включите вкладку. Также похоже, что вы можете рассмотреть CardLayout вместо вкладок. Затем используйте кнопку «Далее» или «Продолжить», когда текущая страница действительна.

person km1    schedule 12.09.2012
comment
Отключение второй вкладки может быть не интуитивно понятным для пользователя. Но макет карты может сделать эту работу. Не забудьте добавить несколько советов для пользователя, чтобы понять, почему он не может перейти к следующему экрану. - person svz; 12.09.2012
comment
Я не хочу отключать какую-либо вкладку. Это означало бы, что мне нужно проверить, действительна ли вкладка после каждого действия, сделанного пользователем, что невозможно. - person Nanocom; 12.09.2012

Похоже, что vetoableChange является частью пакета java.beans. Попробуйте добавить javax.swing.event.ChangeListener.

bodyTabbedPane.addChangeListener(new javax.swing.event.ChangeListener() {
        public void stateChanged(javax.swing.event.ChangeEvent evt) {
            bodyTabbedPaneStateChanged(evt);
        }
    });


private void bodyTabbedPaneStateChanged(javax.swing.event.ChangeEvent evt) {
    if (!isCurrentTabValid()) {             
         throw new PropertyVetoException("test", evt);           
     }
}
person kevin_s    schedule 12.09.2012
comment
думаю, вы не пробовали это ;-) В момент уведомления об изменении выбора выбор уже изменен изменен, так что единственное, что вы можете сделать, это отменить изменение, но ребенок уже мертв после падения в колодец.. - person kleopatra; 12.09.2012