2014-09-01 2 views
2

Это мое второе сообщение, связанное с Java Swing, поэтому простите меня, если мой вопрос слишком прост. Я пытаюсь заставить несколько JPanels общаться друг с другом. Я создаю простую 2D-сетку, в которую я могу добавить стены/заблокированные ячейки, а затем запустить простой алгоритм Floodfill/A * Search (с учетом местоположения старта и цели).Java Swing API - Как могут два JPanels общаться друг с другом?

Чтобы изолировать мою проблему, я решил, что работать с примером будет проще. Поэтому я создал простое приложение, которое позволяет пользователю писать в текстовое поле, при условии, что, что он нажал кнопку «Пуск». После записи в текстовое поле пользователь может нажать кнопку «Пуск», чтобы перевернуть его в состояние «Стоп». В состоянии «Стоп» пользователь не может добавить текст в текстовое поле (т. Е. Приложение не должно регистрировать никаких нажатий клавиш). Это простая проблема, которая действительно раскрывает мой вопрос здесь. Вот как пользовательский интерфейс выглядит как прямо сейчас:

enter image description here

enter image description here

Мой вопрос: Я должен быть в состоянии написать, когда кнопка показывает «Стоп» (так как он находится в режим редактирования), а I не должен иметь возможность писать в текстовой области, когда на кнопке отображается «Пуск» (так как это не режим редактирования). Однако из приведенных выше изображений вы можете видеть, что я могу писать в текстовой области в любом случае. Как мне сделать редактирование текстовой области зависимым от состояния кнопки?

Вот мой код, который пытается настроить это соединение между панелью кнопок и текстовой панелью, но это как-то не работает должным образом.

Я смотрел сообщения StackOverflow here и here, но, откровенно говоря, ответы мне не казались ясными.

SimpleTextPanel:

public class SimpleTextPanel extends JPanel implements PropertyChangeListener, ChangeListener { 


    private boolean canWrite; 
    public SimpleTextPanel() { 

     // set the border properties 
     canWrite = true; 
     TitledBorder title = BorderFactory.createTitledBorder("Simple Text Panel"); 
     title.setTitleColor(Color.BLACK); 
     title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED, 
       Color.DARK_GRAY, Color.GRAY)); 
     this.setBorder(title); 

     JTextArea editSpace = new JTextArea(10, 20); 
     editSpace.setEditable(true); 
     editSpace.addPropertyChangeListener(this); 
     this.add(editSpace); 
    } 

    @Override 
    public void stateChanged(ChangeEvent changeEvent) { 
     JButton button = (JButton)changeEvent.getSource(); 
     canWrite = button.getText().equals("Start"); 
    } 

    @Override 
    public void propertyChange(PropertyChangeEvent propertyChangeEvent) { 
     JTextArea area = (JTextArea)propertyChangeEvent.getSource(); 
     if(!canWrite) area.setText((String)propertyChangeEvent.getOldValue()); 
    } 
} 

SimpleButtonPanel:

public class SimpleButtonPanel extends JPanel implements ActionListener { 

    JButton switchButton; 
    private boolean canWrite = true; 

    public SimpleButtonPanel(SimpleTextPanel txt) { 

     // set the border properties 
     TitledBorder title = BorderFactory.createTitledBorder("Simple Button Panel"); 
     title.setTitleColor(Color.BLACK); 
     title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED, 
       Color.DARK_GRAY, Color.GRAY)); 
     this.setBorder(title); 

     switchButton = new JButton("Start"); 
     switchButton.addActionListener(this); 
     switchButton.addChangeListener(txt); 
     this.add(switchButton); 
    } 

    @Override 
    public void actionPerformed(ActionEvent actionEvent) { 
     if(switchButton.getText().equals("Start")) { 
      switchButton.setText("Stop"); 
      canWrite = false; 
     } else if(switchButton.getText().equals("Stop")) { 
      switchButton.setText("Start"); 
      canWrite = true; 
     } 
    } 
} 

SimpleExampleTest:

public class SimpleExampleTest extends JFrame { 

    public SimpleExampleTest() { 

     setLayout(new BorderLayout()); 
     setTitle("Simple Example"); 
     setSize(300, 300); 
     setLocationRelativeTo(null); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 

     SimpleTextPanel text = new SimpleTextPanel(); 
     SimpleButtonPanel button = new SimpleButtonPanel(text); 

     add(text, BorderLayout.NORTH); 
     add(button, BorderLayout.SOUTH); 
     //button.addPropertyChangeListener(text); // so that text editor knows whether to allow writing or not 
    } 


    public static void main(String[] args) { 

     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       SimpleExampleTest ex = new SimpleExampleTest(); 
       ex.setVisible(true); 
      } 
     }); 
    } 
} 

Любая/вся помощь приветствуется. Спасибо!

+0

Вы можете использовать модель какого-то, чтобы поддерживать динамическую информацию между панелями или предоставить слушатель интерфейс какого-то, что может предупредить редактор оконных изменений состояния кнопки ... – MadProgrammer

+0

вы можете дать SimpleButtonPanel переменная, относящаяся к содержащемуся классу: SimpleExampleTest. Затем в методе actionPerformed вы можете использовать эту переменную для вызова метода в ней, а оттуда выполнить действие в SimpleTextPanel – Stultuske

+0

'public class SimpleTextPanel extends JPanel..' IMO, в котором проблема начинается. Ни одна панель не должна расширять панель, а вместо этого просто быть объектами панели. Элементы управления, которые должны влиять друг на друга, могут быть объявлены в том же классе, что и для панелей, что делает это вопросом. –

ответ

4

Одним из подходов было бы разработать модель, которая могла бы использоваться панелями.

Текстовая панель только хочет знать, что такое текущее состояние и когда это состояние изменяется. Панель кнопок хочет знать, когда изменяется состояние, а также хочет изменить состояние.

Это разъединяет две панели друг от друга, так как они на самом деле не волнует, как состояние изменяется или кто на только что они могут реагировать на изменения соответственно ...

Кроме того, модель Безразлично Не заботьтесь обо всех них, он просто переносит состояние и предоставляет уведомление, когда оно меняется.

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.util.HashSet; 
import java.util.Set; 
import javax.swing.BorderFactory; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JTextArea; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 
import javax.swing.border.BevelBorder; 
import javax.swing.border.TitledBorder; 

public class TalkToEachOther { 

    public static void main(String[] args) { 
     new TalkToEachOther(); 
    } 

    public TalkToEachOther() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame frame = new JFrame("Simple Example"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

       MutableSimpleModel model = new DefaultSimpleModel(); 

       SimpleTextPanel text = new SimpleTextPanel(model); 
       SimpleButtonPanel button = new SimpleButtonPanel(model); 

       frame.add(text, BorderLayout.NORTH); 
       frame.add(button, BorderLayout.SOUTH); 
       //button.addPropertyChangeListener(text); // so that text editor knows whether to allow writing or not 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public interface SimpleModel { 

     public boolean isEditable(); 

     public void addPropertyChangeListener(PropertyChangeListener listener); 
     public void removePropertyChangeListener(PropertyChangeListener listener); 

    } 

    public interface MutableSimpleModel extends SimpleModel { 

     public void setEditable(boolean editable); 

    } 

    public class DefaultSimpleModel implements MutableSimpleModel { 

     private Set<PropertyChangeListener> listeners; 
     private boolean editable; 

     public DefaultSimpleModel() { 
      listeners = new HashSet<>(25); 
     } 

     @Override 
     public void setEditable(boolean value) { 
      if (value != editable) { 
       editable = value; 
       firePropertyChange("editable", !editable, editable); 
      } 
     } 

     @Override 
     public boolean isEditable() { 
      return editable; 
     } 

     @Override 
     public void addPropertyChangeListener(PropertyChangeListener listener) { 
      listeners.add(listener); 
     } 

     @Override 
     public void removePropertyChangeListener(PropertyChangeListener listener) { 
      listeners.remove(listener); 
     } 

     protected void firePropertyChange(String editable, boolean oldValue, boolean newValue) { 
      PropertyChangeEvent evt = new PropertyChangeEvent(this, editable, oldValue, newValue); 
      for (PropertyChangeListener listener : listeners) { 
       listener.propertyChange(evt); 
      } 
     } 

    } 

    public class SimpleTextPanel extends JPanel { 

     private SimpleModel model; 
     private JTextArea editSpace = new JTextArea(10, 20); 

     public SimpleTextPanel(SimpleModel model) { 

      this.model = model; 
      model.addPropertyChangeListener(new PropertyChangeListener() { 
       @Override 
       public void propertyChange(PropertyChangeEvent evt) { 
        updateEditableState(); 
       } 
      }); 
      setLayout(new BorderLayout()); 

      // set the border properties 
      TitledBorder title = BorderFactory.createTitledBorder("Simple Text Panel"); 
      title.setTitleColor(Color.BLACK); 
      title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED, 
          Color.DARK_GRAY, Color.GRAY)); 
      this.setBorder(title); 

      editSpace = new JTextArea(10, 20); 
      this.add(new JScrollPane(editSpace)); 

      updateEditableState(); 
     } 

     protected void updateEditableState() { 
      editSpace.setEditable(model.isEditable()); 
     } 
    } 

    public class SimpleButtonPanel extends JPanel implements ActionListener { 

     private MutableSimpleModel model; 
     private boolean canWrite = true; 

     private JButton switchButton; 

     public SimpleButtonPanel(MutableSimpleModel model) { 

      this.model = model; 
      model.addPropertyChangeListener(new PropertyChangeListener() { 
       @Override 
       public void propertyChange(PropertyChangeEvent evt) { 
        updateEditableState(); 
       } 
      }); 

      // set the border properties 
      TitledBorder title = BorderFactory.createTitledBorder("Simple Button Panel"); 
      title.setTitleColor(Color.BLACK); 
      title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED, 
          Color.DARK_GRAY, Color.GRAY)); 
      this.setBorder(title); 

      switchButton = new JButton("Start"); 
      switchButton.addActionListener(this); 
      this.add(switchButton); 

      updateEditableState(); 
     } 


     @Override 
     public void actionPerformed(ActionEvent actionEvent) { 
      model.setEditable(!model.isEditable()); 
     } 

     protected void updateEditableState() { 
      if (model.isEditable()) { 
       switchButton.setText("Stop"); 
      } else { 
       switchButton.setText("Start"); 
      } 
     } 
    } 
} 
+0

+1 Ran после копирования в мою IDE сразу! Я понимаю, что вы подразумеваете под развязкой: каждый класс знает только, что ему нужно. –

+0

Хорошо, наконец, прошло все.Итак, show stopper здесь - метод MutableSimpleModel # setEditable. Каждый раз, когда запускается действие SimpleButtonPanel actionPerformed(), то есть круг методов PropertyChangeListener # propertyChange для всех слушателей модели. Поскольку SimpleTextPanel нуждается только в уведомлениях от модели, ему не нужно подключать другие свойства PropertyChangeListeners ("editSpace.addPropertyChangeListener (this);" из моего кода в SimpleTextPanel было совершенно не нужно). Аккуратное решение! –

2

Я предлагаю вам создать один подкласс JPanel, который, в свою очередь, содержит другие объекты JPanel. Таким образом, вы можете хранить ссылки на элементы GUI, которые должны взаимодействовать (т. Е. Кнопка и текстовое поле). Теперь кнопка ActionListener может получить доступ к JTextField, считая, что ActionListener является анонимным внутренним классом и что JTextField является переменной-членом.

В более сложной ситуации эта конструкция может быть не идеальной. Однако некоторые из этих концепций будут одинаковыми. В частности, вам нужен родитель JPanel, который облегчает общение между детьми JPanel. Дети JPanel s предоставляют интерфейс, позволяющий это сообщение.Например, у вас может быть TextFieldPanel, который имеет enable() и disable() методов.

+0

Приятный и понятный ответ. Я попробую это сразу. –

+0

Я бы сказал, что нужно «родительскую панель» * для облегчения общения, на самом деле это не ответственность. Некоторая модель, которая поддерживала только информацию о состоянии, требуемую для пользовательского интерфейса, а также возможности уведомления о событиях отделяла бы решение и уменьшала бы раскрытие различных аспектов пользовательского интерфейса в областях, которые просто не нужно знать о них ... но это только мой мнение;) – MadProgrammer

+1

@MadProgrammer Это, безусловно, очень хорошее решение. И, возможно, превосходит мои предложения разными способами. Как и во многих областях программирования, существует несколько способов сделать это, и найти «лучшее» решение часто является вопросом мозгового штурма потенциальных решений и опробования каждого из них. –

Смежные вопросы