我真的觉得这很有趣,并想进一步探索它。从The MVC pattern and Swing(我建议您特别阅读accepted answer)获取指针,让我们尝试一下。
解释:
要遵循的代码摘要(基于上述链接)
登录视图。视图只是您的 UI 组件,具有用于 LoginController 所需的任何组件的 getter 方法以及实现 view 使用的 PropertyChangeListener 来接收PropertyChangeEvents 可以被我们的LoginController 或LoginModel解雇。
LoginController 控制器可以访问 view 和 model。控制器在 views 组件上设置必要的事件,并通过验证来自视图的输入来对它们做出反应,然后要求模型完成它的工作,并可能会改变它的状态。控制器接受您的 views 操作并解释它们。如果您点击一个按钮,控制器的工作就是弄清楚这意味着什么以及应该如何根据该操作操作 模型。
控制器还可以通过将视图(实现PropertyChangeListener)订阅到其SwingPropertyChangeSupport 来请求视图进行更改。当 controller 接收到来自 view 的操作时,在这种情况下,按下 Login 按钮,它会验证来自 view 的输入> 并触发必要的验证错误(如果有)。然后它调用 model 进行实际验证。
LoginModel controller 调用模型上的方法,当其状态发生变化时通知 view .当 model 中的某些内容发生变化时,根据您采取的某些操作(例如单击按钮),模型会通过与 controller 相同的机制通知视图其状态已更改>.
视图也可以向模型询问状态。视图直接从模型中获取它显示的状态。例如,如果我们将 model 实例传递给已经将 authenticated 设置为 true 的视图,您将立即收到消息说您已登录(此处未演示,但需要将 model 传递给 views 构造函数,并在初始化 initView() 之后执行类似 onAuthenticatedPropertyChange(model.isAuthenticated()) 的操作。
一些额外的信息是 controller 和 model 都实现了observer pattern(因为在视图中观察到 controller 和 models 以便对它们做出反应)
控制器还通过SwingWorker在后台线程上执行所有模型,并且所有属性更改侦听器都在EDT上被触发
这是演示上述内容的代码:
TestApp.java:
import javax.swing.SwingUtilities;
public class TestApp {
public TestApp() {
createAndShowGUI();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(TestApp::new);
}
private void createAndShowGUI() {
LoginModel model = new LoginModel();
LoginView view = new LoginView();
LoginController controller = new LoginController(view, model);
}
}
LoginView.java:
import java.awt.Color;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
public class LoginView implements PropertyChangeListener {
private JFrame frame;
private JPanel loginPanel;
private JTextField usernameTextField;
private JTextField passwordTextField;
private JLabel errorLabel;
private JButton loginButton;
public LoginView() {
initView();
}
private void initView() {
frame = new JFrame("TestApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
loginPanel = createLoginPanel();
frame.add(loginPanel);
frame.pack();
frame.setVisible(true);
}
private JPanel createLoginPanel() {
JPanel panel = new JPanel();
panel.setBorder(new EmptyBorder(20, 20, 20, 20));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JLabel usernameLabel = new JLabel("Username:");
usernameLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
usernameTextField = new JTextField();
usernameTextField.setAlignmentX(Component.CENTER_ALIGNMENT);
usernameTextField.setColumns(20);
JLabel passwordLabel = new JLabel("Password:");
passwordLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
passwordTextField = new JTextField();
passwordTextField.setAlignmentX(Component.CENTER_ALIGNMENT);
passwordTextField.setColumns(20);
loginButton = new JButton("Login");
loginButton.setAlignmentX(Component.CENTER_ALIGNMENT);
errorLabel = new JLabel();
errorLabel.setForeground(Color.RED);
errorLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
errorLabel.setVisible(false);
panel.add(usernameLabel);
panel.add(usernameTextField);
panel.add(passwordLabel);
panel.add(passwordTextField);
panel.add(errorLabel);
panel.add(loginButton);
return panel;
}
public JButton getLoginButton() {
return loginButton;
}
public JTextField getUsernameTextField() {
return usernameTextField;
}
public JTextField getPasswordTextField() {
return passwordTextField;
}
public JFrame getFrame() {
return frame;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
Object newValue = evt.getNewValue();
switch (propertyName) {
case "authenticated":
// authentication has finished lets do something
onAuthenticatedPropertyChange((Boolean) newValue);
break;
case "authenticating":
// lets disable the UI as we authenticate
errorLabel.setVisible(false);
disableLoginPanelComponents(true);
break;
case "usernameInvalid":
showError((String) newValue);
break;
case "passwordInvalid":
showError((String) newValue);
break;
}
}
private void onAuthenticatedPropertyChange(Boolean authenticated) {
if (authenticated == true) {
errorLabel.setVisible(false);
JOptionPane.showMessageDialog(frame, "You are in!");
} else {
showError("Invalid username or password!");
}
// re-enable components after authentication regadless of fail or pass
disableLoginPanelComponents(false);
}
private void showError(String error) {
errorLabel.setText(error);
errorLabel.setVisible(true);
}
private void disableLoginPanelComponents(boolean disable) {
for (int i = 0; i < loginPanel.getComponentCount(); i++) {
Component component = (Component) loginPanel.getComponent(i);
if (component instanceof JTextField || component instanceof JButton) {
((JComponent) loginPanel.getComponent(i)).setEnabled(!disable);
}
}
}
}
登录控制器:
import java.awt.event.ActionEvent;
import javax.swing.SwingWorker;
import javax.swing.event.SwingPropertyChangeSupport;
public class LoginController {
private final SwingPropertyChangeSupport propertyChangeSupport;
private final LoginView view;
private final LoginModel model;
public LoginController(LoginView view, LoginModel model) {
this.view = view;
this.model = model;
propertyChangeSupport = new SwingPropertyChangeSupport(this, true);
initController();
}
private void initController() {
// make the view a listener of both the model and controller as both can send events which the view must react too
model.addPropertyChangeListener(view);
propertyChangeSupport.addPropertyChangeListener(view);
view.getLoginButton().addActionListener((ActionEvent e) -> {
// lets do some basic validation of username and password fields
String username = view.getUsernameTextField().getText();
String password = view.getPasswordTextField().getText();
// validate user input before sending it to the model (this is the contorllers job - besdoes business rules i..e username doesnt exist etc)
if (username.isEmpty()) {
propertyChangeSupport.firePropertyChange("usernameInvalid", null, "Username cannot be empty!");
return;
}
if (password.isEmpty()) {
propertyChangeSupport.firePropertyChange("passwordInvalid", null, "Password cannot be empty!");
return;
}
// call the model to login on a background thread as the model may query the db or hit an API etc
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
model.login(username, password);
return null;
}
}.execute();
});
}
}
LoginModel.java:
import java.beans.PropertyChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.SwingPropertyChangeSupport;
public class LoginModel {
private final SwingPropertyChangeSupport propertyChangeSupport;
private boolean authenticated;
public LoginModel() {
propertyChangeSupport = new SwingPropertyChangeSupport(this, true); // we pass in true to noifty observers on the Event Dispatch Thread
}
public void login(String username, String password) {
propertyChangeSupport.firePropertyChange("authenticating", null, true);
// lets simulate query a database or something for 3 seconds
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(LoginModel.class.getName()).log(Level.SEVERE, null, ex);
}
authenticated = username.equals("admin") && password.equals("password");
propertyChangeSupport.firePropertyChange("authenticated", null, authenticated);
}
public void addPropertyChangeListener(PropertyChangeListener prop) {
propertyChangeSupport.addPropertyChangeListener(prop);
}
public boolean isAuthenticated() {
return authenticated;
}
}
以上内容是通用的,并没有直接回答您的问题,而是为您提供了 Swing 中 MVC 模式的示例。
更新:
这是一个针对您的问题的示例
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.filechooser.FileNameExtensionFilter;
public class TestApp {
public TestApp() {
createAndShowGUI();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(TestApp::new);
}
private void createAndShowGUI() {
DisplayImageModel model = new DisplayImageModel();
DisplayImageView view = new DisplayImageView();
DisplayImageController controller = new DisplayImageController(view, model);
}
class DisplayImageView implements PropertyChangeListener {
private JFrame frame;
private ImagePanel imagePanel;
private JLabel imageLabel;
private JButton pickImageButton;
public DisplayImageView() {
initView();
}
private void initView() {
frame = new JFrame("TestApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = createImagePanel();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
private JPanel createImagePanel() {
JPanel panel = new JPanel();
panel.setBorder(new EmptyBorder(20, 20, 20, 20));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
pickImageButton = new JButton("Pick an image");
pickImageButton.setAlignmentX(Component.CENTER_ALIGNMENT);
imagePanel = new ImagePanel();
imagePanel.getPanel().setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(imagePanel.getPanel());
panel.add(pickImageButton);
return panel;
}
public JButton getPickAnImageButton() {
return pickImageButton;
}
public JLabel getImageLabel() {
return imageLabel;
}
public JFrame getFrame() {
return frame;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
Object newValue = evt.getNewValue();
switch (propertyName) {
case "image":
onImagePropertyChange((BufferedImage) newValue);
break;
}
}
private void onImagePropertyChange(BufferedImage image) {
imagePanel.setImage(image);
frame.pack();
}
}
class DisplayImageModel {
private final SwingPropertyChangeSupport propertyChangeSupport;
private BufferedImage image;
public DisplayImageModel() {
propertyChangeSupport = new SwingPropertyChangeSupport(this, true); // we pass in true to noifty observers on the Event Dispatch Thread
}
public void setImage(BufferedImage image) {
this.image = image;
propertyChangeSupport.firePropertyChange("image", null, image);
}
public void addPropertyChangeListener(PropertyChangeListener prop) {
propertyChangeSupport.addPropertyChangeListener(prop);
}
public BufferedImage getImage() {
return image;
}
}
class DisplayImageController {
private final SwingPropertyChangeSupport propertyChangeSupport;
private final DisplayImageView view;
private final DisplayImageModel model;
public DisplayImageController(DisplayImageView view, DisplayImageModel model) {
this.view = view;
this.model = model;
propertyChangeSupport = new SwingPropertyChangeSupport(this, true);
initController();
}
private void initController() {
// make the view a listener of both the model and controller as both can send events which the view must react too
model.addPropertyChangeListener(view);
propertyChangeSupport.addPropertyChangeListener(view);
view.getPickAnImageButton().addActionListener((ActionEvent e) -> {
JFileChooser chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter(
"JPG & PNG Images", "jpg", "png");
chooser.addChoosableFileFilter(filter);
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int result = chooser.showOpenDialog(view.getFrame());
if (result == JFileChooser.APPROVE_OPTION) {
File selectedFile = chooser.getSelectedFile();
BufferedImage image;
try {
image = ImageIO.read(selectedFile);
model.setImage(image);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
}
class ImagePanel {
private final JPanel panel;
private BufferedImage image;
public ImagePanel() {
this.panel = new JPanel() {
@Override
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
} else {
return new Dimension(200, 200);
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, this);
}
}
};
panel.setBorder(new LineBorder(Color.GRAY, 1));
}
public void setImage(BufferedImage image) {
this.image = image;
panel.revalidate();
panel.repaint();
}
public JPanel getPanel() {
return panel;
}
}
}