Допустим, я создаю Java Swing GUI, и у меня есть фрейм, который содержит панель, содержащую другую панель, которая содержит кнопку.(Предположим, что панели можно использовать многократно, поэтому я разделил их на отдельные классы.)
Frame → FirstPanel → SecondPanel → Button
На самом деле линия детей может быть более сложной, но я просто хочу сохранить этот пример простым.
Если я хочу, чтобы кнопка управляла одним из своих родительских компонентов (например, изменяет размер фрейма), каков наилучший способ реализации функциональности между двумя классами графического интерфейса, которые не обязательно находятся один непосредственно внутри другого?
Мне не нравится идея связать вместе getParent()
методы или передать экземпляр Frame
через дочерние элементы так, чтобы к нему можно было обращаться из SecondPanel
.По сути, я не хочу, чтобы мои классы были последовательно соединены так или иначе.
Это экземпляр, в котором кнопка должна обновлять модель, а не родительский компонент напрямую?Затем родитель получает уведомление об изменении модели и обновляется соответствующим образом?
Я собрал небольшой пример, который должен компилироваться и запускаться самостоятельно, чтобы проиллюстрировать мою проблему.Это два JButton в JPanel, в другом JPanel, в JFrame.Кнопки управляют размером JFrame.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MVCExample
{
public static void main(String[] args)
{
Model model = new Model();
Controller ctrl = new Controller();
ctrl.registerModel(model);
View view = new View(ctrl);
view.setVisible(true);
model.init();
}
/**
* Model class
*/
static class Model
{
private ArrayList<PropertyChangeListener> listeners =
new ArrayList<PropertyChangeListener>();
private Dimension windowSize;
public Dimension getWindowSize(){ return windowSize; }
public void setWindowSize(Dimension windowSize)
{
if(!windowSize.equals(getWindowSize()))
{
firePropertyChangeEvent(getWindowSize(), windowSize);
this.windowSize = windowSize;
}
}
public void init()
{
setWindowSize(new Dimension(400, 400));
}
public void addListener(PropertyChangeListener listener)
{
listeners.add(listener);
}
public void firePropertyChangeEvent(Object oldValue, Object newValue)
{
for(PropertyChangeListener listener : listeners)
{
listener.propertyChange(new PropertyChangeEvent(
this, null, oldValue, newValue));
}
}
}
/**
* Controller class
*/
static class Controller implements PropertyChangeListener
{
private Model model;
private View view;
public void registerModel(Model model)
{
this.model = model;
model.addListener(this);
}
public void registerView(View view)
{
this.view = view;
}
// Called from view
public void updateWindowSize(Dimension windowSize)
{
model.setWindowSize(windowSize);
}
// Called from model
public void propertyChange(PropertyChangeEvent pce)
{
view.processEvent(pce);
}
}
/**
* View classes
*/
static class View extends JFrame
{
public View(Controller ctrl)
{
super("JFrame");
ctrl.registerView(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(new FirstPanel(ctrl));
pack();
}
public void processEvent(PropertyChangeEvent pce)
{
setPreferredSize((Dimension)pce.getNewValue());
pack();
}
}
static class FirstPanel extends JPanel
{
public FirstPanel(Controller ctrl)
{
setBorder(BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(
Color.RED, 2), "First Panel"));
add(new SecondPanel(ctrl));
}
}
static class SecondPanel extends JPanel
{
private Controller controller;
private JButton smallButton = new JButton("400x400");
private JButton largeButton = new JButton("800x800");
public SecondPanel(Controller ctrl)
{
this.controller = ctrl;
setBorder(BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(
Color.BLUE, 2), "Second Panel"));
add(smallButton);
add(largeButton);
smallButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
controller.updateWindowSize(new Dimension(400, 400));
}
});
largeButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
controller.updateWindowSize(new Dimension(800, 800));
}
});
}
}
}
Что мне не нравится, так это то, что контроллер должен существовать в JFrame, чтобы фрейм мог регистрироваться для получения событий.Но затем контроллер должен быть полностью передан на SecondPanel (строки 112, 131 и 143), чтобы панель могла взаимодействовать с моделью.
Мне кажется, что здесь происходит что-то неэффективное(и классы становятся слишком тесно связанными).Дайте мне знать, если моя проблема не ясна.