问题
I am creating to learn MVC in Java. I read that Java app with few JFrames is bad idea and better solution is to use JPanel. JDialog also does not solve the problem, because then I have too many of them. I have the MainFrame with a few buttons and my question is - how to change JPanels on the Frame after click on one these buttons? I know how to change view if the View is Frame...
//ActionListener in the MainController
public void actionPerformed(ActionEvent e) {
TheView theView2 = new TheView(); //TheView extends JFrame
TheModel theModel2 = new TheModel();
TheController theController = new TheController(theView2,theModel2);
theView.setVisible(false);
theView2.setVisible(true);
}
..but I have no idea, how to change only panels on the one frame? I know, that i have to add the listener to the button in the View and then in Controller add the rest of code (like above), but what do I need to change the view in the Controller? And should I do it in MainController or in the MainFrameController? Below is how to this app should work:
- At the beginning is showing up the MainFrame with visible Panel1.
- User click Button1.
- Othe same MainFrame is showing Panel2.
And so on with Panel3, 4...
Thanks for help.
EDIT: Yes, I know CardLayout and TabbedPane and I can use them, but I don't know how to apply this solution in MVC. "I just need answer how specifically should look like this piece of code with the change of the JPanel." And where should it be. In the MainController? In the MainFrameController?
回答1:
I just need answer how specifically should look like this piece of code with the change of the JPanel. I wrote that I can use CardLayout WITHOUT MVC, but I have no idea how to do it in MVC... How to apply it to the MainFrame?
Your controller and model code shouldn't care what form the view takes, whether it extends JFrame, JPanel, or (probably best) neither. Presumably in your control's actionPerformed method above, the view, model and controller have already been created and appropriately "connected". In this actionPerformed method, all you'd do would be to send a command to the model to change its state. In other words, the controller would call a method on the model that would initiate a state change. I usually have the view then respond by listening for changes to the model.
This has not yet been fully "wired", but I imagine that sample code could look something like the code below. Note that I prefer to use anonymous inner class listeners that are part of the view and that themselves call Controller methods:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class SimpleMVC {
private static void createAndShowGui() {
SmvcView view = new SmvcView();
SmvcModel model = new SmvcModel();
new SmvcController(view, model);
view.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
@SuppressWarnings("serial")
class SmvcView {
private JPanel mainPanel = new JPanel();
private CardLayout cardLayout = new CardLayout();
private JPanel cardPanel = new JPanel(cardLayout);
private ModelListener modelListener = new ModelListener();
private SmvcController controller;
private DefaultComboBoxModel<SmvcState> comboModel = new DefaultComboBoxModel<>(
SmvcState.values());
private JComboBox<SmvcState> stateCombo = new JComboBox<>(comboModel);
public SmvcView() {
stateCombo.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
DefaultListCellRenderer renderer = (DefaultListCellRenderer) super
.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
SmvcState state = (SmvcState) value;
if (state != null) {
value = state.getText();
renderer = (DefaultListCellRenderer) super.getListCellRendererComponent(list,
value, index, isSelected, cellHasFocus);
}
return renderer;
}
});
stateCombo.addActionListener(evt -> {
SmvcState state = (SmvcState) stateCombo.getSelectedItem();
controller.setModelState(state);
});
JPanel comboPanel = new JPanel();
comboPanel.add(stateCombo);
cardPanel.add(new IntroPanel(), SmvcState.INTRO.toString());
cardPanel.add(new MainPanel(), SmvcState.MAIN.toString());
cardPanel.add(new MenuPanel(), SmvcState.MENU.toString());
mainPanel.setLayout(new BorderLayout());
mainPanel.add(cardPanel, BorderLayout.CENTER);
mainPanel.add(comboPanel, BorderLayout.PAGE_END);
}
public PropertyChangeListener getModelListener() {
return modelListener;
}
public void setController(SmvcController controller) {
this.controller = controller;
}
public void setVisible(boolean b) {
JFrame frame = new JFrame("Simple MVC");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class ModelListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (SmvcModel.STATE.equals(evt.getPropertyName())) {
SmvcState state = (SmvcState) evt.getNewValue();
cardLayout.show(cardPanel, state.toString());
}
}
}
private class IntroPanel extends JPanel {
public IntroPanel() {
setPreferredSize(new Dimension(400, 300));
setLayout(new GridBagLayout());
add(new JLabel(SmvcState.INTRO.toString()), SwingConstants.CENTER);
setBorder(BorderFactory.createTitledBorder(SmvcState.INTRO.toString()));
}
}
private class MainPanel extends JPanel {
public MainPanel() {
setPreferredSize(new Dimension(400, 300));
setLayout(new GridBagLayout());
add(new JLabel(SmvcState.MAIN.toString()), SwingConstants.CENTER);
setBorder(BorderFactory.createTitledBorder(SmvcState.MAIN.toString()));
}
}
private class MenuPanel extends JPanel {
public MenuPanel() {
setPreferredSize(new Dimension(400, 300));
setLayout(new GridBagLayout());
add(new JLabel(SmvcState.MENU.toString()), SwingConstants.CENTER);
setBorder(BorderFactory.createTitledBorder(SmvcState.MENU.toString()));
}
}
}
class SmvcController {
private SmvcView view;
private SmvcModel model;
public SmvcController(SmvcView view, SmvcModel model) {
this.view = view;
this.model = model;
view.setController(this);
model.addPropertyChangeListener(view.getModelListener());
}
public void setModelState(SmvcState state) {
model.setState(state);
}
public SmvcView getView() {
return view;
}
public SmvcModel getModel() {
return model;
}
}
class SmvcModel {
public static final String STATE = "state";
private SwingPropertyChangeSupport support = new SwingPropertyChangeSupport(this);
private SmvcState state = SmvcState.INTRO;
public void addPropertyChangeListener(PropertyChangeListener modelListener) {
support.addPropertyChangeListener(modelListener);
}
public SmvcState getState() {
return state;
}
// notify listeners whenever state is changed
public void setState(SmvcState state) {
SmvcState oldValue = this.state;
SmvcState newValue = state;
this.state = state;
support.firePropertyChange(STATE, oldValue, newValue);
}
}
enum SmvcState {
INTRO("Intro"), MENU("Menu"), MAIN("Main");
private String text;
private SmvcState(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
回答2:
What you could use is CardLayout or JTabbedPane. The linked tutorials should get you started.
来源:https://stackoverflow.com/questions/39310850/java-mvc-how-to-change-view-jpanel