如何在 Java Swing MVC 架构中显示图像? [英] How do I display an Image in Java Swing MVC architecture?

查看:24
本文介绍了如何在 Java Swing MVC 架构中显示图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

In my java application, my objective is to display or output an image using MVC architecture. My java application is comprised of an imagecontroller(main), imageview, and image model. I am currently able to select an image, the compiler acknowledges where the image has been selected from in the c: drive however it does not output or display the image. Here is a copy of my code below:

package imagecontroller;

import javax.swing.SwingUtilities;
import java.io.File;

    public class ImageController {
    
    private final ImageModel model;
    private final ImageView view; 

public ImageController () {
    this.view = new ImageView(this);
    this.model = new ImageModel(this.view);

public static void launch () {
    new ImageController();

}
    public static void main(String[] args) {
    SwingUtilities.invokeLater(ImageController::launch);   
    } 
}   


package imagecontroller;

import java.awt.Dimension;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileNameExtensionFilter;    

      public class ImageView extends JFrame{
       
     private final ImageController controller;
    
        public ImageView(ImageController controller) {
            this.controller = controller;
    
            JFileChooser Chooser = new JFileChooser();
            FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & PNG Images", "jpg", "png");
            Chooser.addChoosableFileFilter(filter);
            Chooser.setCurrentDirectory(new File(System.getProperty("user.home")));
            Chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            int result 

      

         Chooser.showOpenDialog(null);
            if (result == JFileChooser.APPROVE_OPTION) {
                Chooser.setAcceptAllFileFilterUsed(true);
                File selectedFile = Chooser.getSelectedFile();
                System.out.println("Selected file: " + selectedFile.getAbsolutePath());
    
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.setTitle("ImageAnnotator");
                frame.setVisible(true);
                frame.setSize(new Dimension (500,500));
        
        } 
        
        }
    }


package imagecontroller;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
    
    
        public class ImageModel extends JComponent  {    
            private final ImageView view;
            private BufferedImage image;
           
            public ImageModel(ImageView view) {
                this.view = view;
            }
            public void CustomComponent (File png) {
                BufferedImage image = null; 
                setPreferredSize(new Dimension(400, 400));
              try {
                    this.image = ImageIO.read(new File("640px-Pleiades_large.png"));
                } catch (IOException x) {
                    JOptionPane.showMessageDialog(null, "Not an ImageFile, Please Select an Image");
                }
            }
            @Override
             public void paintComponent(Graphics g){
                super.paintComponent(g);
                g = g.create();
                g.setColor(Color.WHITE);
                g.fillRect(0, 0, getWidth(), getHeight());
                int margin = 20;
                int w = (getWidth() - (2 * margin + 2)) / 2;
                int h = this.image.getHeight() * w / this.image.getWidth();
                g.drawImage(image, h, h, WIDTH, HEIGHT, view);
        
        }
        }

解决方案

I really found this interesting and thought to explore it a little more. Taking pointers from The MVC pattern and Swing (which I suggest you read especially the accepted answer) lets try have a go.

Explanation:

Brief Summary of the code to follow (based off of the above link)

The LoginView. The view is simply your UI components, with getter methods for any components needed by the LoginController as well as implementing a PropertyChangeListener which is used by the view to receive PropertyChangeEvents which could be fired by our LoginController or LoginModel.

The LoginController The controller has access to both the view and the model. The controller sets up the necessary events on the views components and reacts to them by validating input from the view and then asks the model to do its job and potentially that will change its state. The controller takes your views actions and interprets them. If you click on a button, it's the controller's job to figure out what that means and how the model should be manipulated based on that action.

The controller may also ask the view to change it does this by subscribing the view (which implements PropertyChangeListener) to its SwingPropertyChangeSupport. When the controller receives an action from the view, in this case the Login button pressed it validates inputs from the view and fires the necessary validation errors (if any). It then calls the model to do the actual validation.

The LoginModel The controller calls methods on the model which intern notifies the view when its state has changed. When something changes in the model, based either on some action you took (like clicking a button) the model notifies the view that its state has changed through the same mechanism as the controller.

The view can also ask the model for state. The view gets the state it displays directly from the model. For instance, if we pass in a model instance to our view which already has authenticated set to true, you will immediately receive the message to say you are logged in (this has not been demonstrated here, but would entail passing the model into the views constructor and after initializing the initView() doing something like onAuthenticatedPropertyChange(model.isAuthenticated())).

Some extra information is both controller and model implement the observer pattern (as in the view observes changes to the controller and models in order to react to them) The controller also does all model execution on a background thread via a SwingWorker, and all property change listeners are fired back on the EDT

Here is the code which demonstrates the above:

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);
            }
        }
    }

}

LoginController:

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;
    }
}

The above is generic and doesn't directly answer YOUR question but gives you an example of an MVC pattern in Swing.

Update:

Here is an example specific to your issue

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;
        }
    }

}

这篇关于如何在 Java Swing MVC 架构中显示图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆