Utilizzo di JFileChooser con le classi e gli ascoltatori della GUI Swing

Questo è il mio menu corrente:

public class DrawPolygons { public static void main (String[] args) throws FileNotFoundException { /** * Menu - file reader option */ JMenuBar menuBar; JMenu menu; JMenuItem menuItem; // Create the menu bar. menuBar = new JMenuBar(); // Build the first menu. menu = new JMenu("File"); menu.setMnemonic(KeyEvent.VK_F); menu.getAccessibleContext().setAccessibleDescription("I have items"); menuBar.add(menu); // a group of JMenuItems menuItem = new JMenuItem("Load",KeyEvent.VK_T); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK)); menuItem.getAccessibleContext().setAccessibleDescription("Load your old polygons"); menu.add(menuItem); menuItem = new JMenuItem("Save",KeyEvent.VK_U); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK)); menuItem.getAccessibleContext().setAccessibleDescription("Save the contents of your polygons"); menu.add(menuItem); // attaching the menu to the frame JFrame frame = new JFrame("Draw polygons"); frame.setJMenuBar(menuBar); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(new DrawingPanel()); frame.pack(); frame.setVisible(true); } } 

Ha due opzioni per Load e Save . inserisci la descrizione dell'immagine qui

Ora, come posso colbind JFileChooser al metodo actionPerformsd , qui:

 /** * Main class * @author X2 * */ class DrawingPanel extends JPanel implements MouseListener, MouseMotionListener ,KeyListener { // code // code // and more code static DrawingPanel app ; private static final Dimension MIN_DIM = new Dimension(300, 300); private static final Dimension PREF_DIM = new Dimension(500, 500); public Dimension getMinimumSize() { return MIN_DIM; } public Dimension getPreferredSize() { return PREF_DIM; } JMenuItem open, save; JTextArea textArea ; JFileChooser chooser ; FileInputStream fis ; BufferedReader br ; FileOutputStream fos ; BufferedWriter bwriter ; public void actionPerformsd( ActionEvent event ) { Object obj = event.getSource() ; chooser = new JFileChooser() ; if ( chooser.showOpenDialog( app ) == JFileChooser.APPROVE_OPTION ) if ( obj == open ) { try { fis = new FileInputStream( chooser.getSelectedFile() ) ; br = new BufferedReader( new InputStreamReader( fis ) ) ; String read ; StringBuffer text = new StringBuffer() ; while( ( read = br.readLine() ) != null ) { text.append( read ).append( "\n" ) ; } textArea.setText( text.toString() ) ; } catch( IOException e ) { JOptionPane.showMessageDialog( this , "Error in File Operation" ,"Error in File Operation" ,JOptionPane.INFORMATION_MESSAGE) ; } } } /** * The constructor */ DrawingPanel() { super(); addMouseListener(this); addMouseMotionListener(this); addKeyListener(this); setFocusable(true); requestFocusInWindow(); } // a lot of code more // and more // and more } 

Con il codice iniziale del menu e il Jpanel , che ho creato in main ?

Saluti

————————

MODIFICARE:

Il “nuovo” codice:

 public class DrawPolygons { public static void main (String[] args) throws FileNotFoundException { // attaching the menu to the frame JFrame frame = new JFrame("Draw polygons"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // JMenuBar // Create the menu and JmenuBar JMenuBar menuBar = new JMenuBar(); // Build the first menu. JMenu menu = new JMenu("File"); menu.setMnemonic(KeyEvent.VK_F); menu.getAccessibleContext().setAccessibleDescription("I have items"); menuBar.add(menu); // menu option - load // create the load option final JMenuItem loadItem = new JMenuItem("Load",KeyEvent.VK_T); // add the shortcut loadItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK)); // short description loadItem.getAccessibleContext().setAccessibleDescription("Load your old polygons"); // JFileChooser with filter JFileChooser fileChooser = new JFileChooser("."); // apply the filter to file chooser FileNameExtensionFilter filter = new FileNameExtensionFilter("scn files (*.scn)", "scn"); fileChooser.setFileFilter(filter); fileChooser.setControlButtonsAreShown(false); frame.add(fileChooser, BorderLayout.CENTER); final JLabel directoryLabel = new JLabel(" "); directoryLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 36)); final JLabel filenameLabel = new JLabel(" "); filenameLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 36)); // add listener to LOAD loadItem.addActionListener( new ActionListener() { public void actionPerformsd(ActionEvent actionEvent) { JFileChooser theFileChooser = new JFileChooser(); String command = actionEvent.getActionCommand(); if (command.equals(JFileChooser.APPROVE_SELECTION)) { File selectedFile = theFileChooser.getSelectedFile(); directoryLabel.setText(selectedFile.getParent()); filenameLabel.setText(selectedFile.getName()); } else if (command.equals(JFileChooser.CANCEL_SELECTION)) { directoryLabel.setText(" "); filenameLabel.setText(" "); } }} // end listener ); // end listener to loadItem menu.add(loadItem); // now SAVE // create the option for save JMenuItem saveItem = new JMenuItem("Save",KeyEvent.VK_U); // key shortcut for save saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK)); saveItem.getAccessibleContext().setAccessibleDescription("Save the contents of your polygons"); // add the save to the menu menu.add(saveItem); frame.setJMenuBar(menuBar); frame.setContentPane(new DrawingPanel()); frame.pack(); frame.setVisible(true); } } 

Il problema è, ora, quando premo Load sotto File , non succede nulla. Perché ?

Ho aggiunto l’ascoltatore, ma niente.

Come regola generale, non dovresti avere le tue classi GUI, come la class che estende JPanel, implementare qualsiasi interfaccia listener, e in effetti dovresti cercare il contrario – separare le funzioni di controllo del programma (gli ascoltatori e il come) dalle funzioni di visualizzazione del programma (la GUI). Quindi la mia risposta alla tua domanda su “come posso colbind JFileChooser al metodo actionPerformsd … [alla mia class DrawingPanel che estende JPanel], è di sforzarmi di non farlo.

Invece, le classi di vista implementano interfacce che consentono alle classi di controllo di interagire più facilmente con esse.

Modifica 1 : il tuo nuovo codice non visualizza mai la finestra di dialogo JFileChooser. È necessario visualizzare la finestra di dialogo aperta:

 // first make sure that you've declared the JFrame frame as final int result = theFileChooser.showOpenDialog(frame); if (result == JFileChooser.APPROVE_OPTION) { // ... code goes here } 

Modifica 2

Esempio di Swing Model-View-Control:

Ad esempio, ecco un’implementazione MVC o Model-View-Control che mostra la vista, il modello e il controllo. Tutto ciò è molto semplicistico, e tutto ciò che fa attualmente è aprire un file di testo e visualizzarlo in un JTextField, il gioco è fatto, e cerca di separare le funzioni di controllo dalla vista.

Classe MvcMain

 import javax.swing.SwingUtilities; public class MvcMain { private static void createAndShowGui() { MvcView view = new ShowTextView("Show Text"); MvcModel model = new ShowTextModel(); ShowTextControl control = new ShowTextControl(view, model); control.showView(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } 

Interfaccia MvcModel

 import java.beans.PropertyChangeListener; public interface MvcModel { static final String TEXT = "text"; static final String STATUS = "STATUS"; String getText(); String getStatus(); void setText(String text); void setStatus(String text); void addPropertyChangeListener(PropertyChangeListener listener); void removePropertyChangeListener(PropertyChangeListener listener); } 

Interfaccia MvcView

 import java.awt.Window; import javax.swing.Action; public interface MvcView { void setVisible(boolean visible); void setFileAction(Action fileAction); void setOpenFileAction(Action openFileAction); void setSaveToFileAction(Action saveToFileAction); void setExitAction(Action exitAction); void setStatusText(String string); String getTextAreaText(); void setTextAreaText(String text); Window getTopWindow(); } 

Classe ShowTextView

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.Window; import javax.swing.*; public class ShowTextView implements MvcView { private JFrame frame = new JFrame(); private JMenuBar menuBar = new JMenuBar(); private JMenu fileMenu = new JMenu(); private StatusBar statusBar = new StatusBar(); private ViewDisplayText displayText = new ViewDisplayText(); public ShowTextView(String title) { menuBar.add(fileMenu); frame.setTitle(title); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(displayText.getMainComponent(), BorderLayout.CENTER); frame.getContentPane().add(statusBar.getComponent(), BorderLayout.PAGE_END); frame.setJMenuBar(menuBar); } @Override public void setVisible(boolean visible) { frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } @Override public void setOpenFileAction(Action action) { displayText.setOpenFileButtonAction(action); fileMenu.add(new JMenuItem(action)); } @Override public void setSaveToFileAction(Action action) { displayText.setSaveToFileAction(action); fileMenu.add(new JMenuItem(action)); } @Override public void setExitAction(Action exitAction) { displayText.setExitAction(exitAction); fileMenu.add(new JMenuItem(exitAction)); } @Override public void setFileAction(Action fileAction) { fileMenu.setAction(fileAction); } @Override public String getTextAreaText() { return displayText.getTextAreaText(); } @Override public void setTextAreaText(String text) { displayText.setTextAreaText(text); } @Override public Window getTopWindow() { return frame; } @Override public void setStatusText(String text) { statusBar.setText(text); } } class ViewDisplayText { private static final int TA_ROWS = 30; private static final int TA_COLS = 50; private static final int GAP = 2; private JPanel mainPanel = new JPanel(); private JButton openFileButton = new JButton(); private JButton saveToFileButton = new JButton(); private JButton exitButton = new JButton(); private JTextArea textArea = new JTextArea(TA_ROWS, TA_COLS); public ViewDisplayText() { JPanel buttonPanel = new JPanel(new GridLayout(1, 0, GAP, 0)); buttonPanel.add(openFileButton); buttonPanel.add(saveToFileButton); buttonPanel.add(exitButton); mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); mainPanel.setLayout(new BorderLayout()); mainPanel.add(new JScrollPane(textArea), BorderLayout.CENTER); mainPanel.add(buttonPanel, BorderLayout.PAGE_END); } public void setExitAction(Action exitAction) { exitButton.setAction(exitAction); } public JComponent getMainComponent() { return mainPanel; } public void setOpenFileButtonAction(Action action) { openFileButton.setAction(action); } public void setSaveToFileAction(Action action) { saveToFileButton.setAction(action); } public String getTextAreaText() { return textArea.getText(); } public void setTextAreaText(String text) { textArea.setText(text); } } class StatusBar { private static final String STATUS = "Status: "; private JLabel label = new JLabel(STATUS); public StatusBar() { label.setBorder(BorderFactory.createLineBorder(Color.black)); } public JComponent getComponent() { return label; } public void setText(String text) { label.setText(STATUS + text); } } 

Classe ShowTextModel

 import java.beans.PropertyChangeListener; import javax.swing.event.SwingPropertyChangeSupport; public class ShowTextModel implements MvcModel { private String text; private String status; private SwingPropertyChangeSupport propChangeSupport = new SwingPropertyChangeSupport(this); @Override public String getText() { return text; } @Override public void setText(String text) { String newValue = text; String oldValue = this.text; this.text = newValue; propChangeSupport.firePropertyChange(TEXT, oldValue, newValue); } @Override public void setStatus(String status) { String newValue = status; String oldValue = this.status; this.status = newValue; propChangeSupport.firePropertyChange(STATUS, oldValue, newValue); } @Override public void addPropertyChangeListener(PropertyChangeListener listener) { propChangeSupport.addPropertyChangeListener(listener); } @Override public void removePropertyChangeListener(PropertyChangeListener listener) { propChangeSupport.removePropertyChangeListener(listener); } @Override public String getStatus() { return status; } } 

Classe ShowTextControl

 import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; import java.util.concurrent.ExecutionException; import javax.swing.*; public class ShowTextControl { private MvcView view; private MvcModel model; public ShowTextControl(MvcView view, MvcModel model) { this.view = view; this.model = model; view.setFileAction(new FileAction("File", KeyEvent.VK_F)); view.setOpenFileAction(new OpenFileAction(view, model, "Open File", KeyEvent.VK_O)); view.setSaveToFileAction(new SaveToFileAction(view, model, "Save to File", KeyEvent.VK_S)); view.setExitAction(new ExitAction(view, model, "Exit", KeyEvent.VK_X)); model.addPropertyChangeListener(new ModelListener(view, model)); } public void showView(boolean visible) { view.setVisible(visible); } } @SuppressWarnings("serial") class OpenFileAction extends AbstractAction { private MvcView view; private MvcModel model; public OpenFileAction(MvcView view, MvcModel model, String name, int keyCode) { super(name); putValue(MNEMONIC_KEY, keyCode); this.view = view; this.model = model; } @Override public void actionPerformsd(ActionEvent evt) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setMultiSelectionEnabled(false); int result = fileChooser.showOpenDialog(view.getTopWindow()); if (result == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); if (file.exists()) { if (file.getName().endsWith(".txt")) { model.setStatus("Opening file \"" + file.getName() + "\""); OpenFileWorker openFileWorker = new OpenFileWorker(file); openFileWorker.addPropertyChangeListener( new OpenFileWorkerListener(model)); openFileWorker.execute(); } else { model.setStatus("File \"" + file.getName() + "\" is not a text file"); } } else { model.setStatus("File \"" + file.getName() + "\" does not exist"); } } } } class OpenFileWorker extends SwingWorker { private File file; public OpenFileWorker(File file) { this.file = file; } public File getFile() { return file; } @Override protected String doInBackground() throws Exception { StringBuilder stringBuilder = new StringBuilder(); Scanner scan = null; try { scan = new Scanner(file); while (scan.hasNextLine()) { stringBuilder.append(scan.nextLine() + "\n"); } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (scan != null) { scan.close(); } } return stringBuilder.toString(); } } class OpenFileWorkerListener implements PropertyChangeListener { private MvcModel model; public OpenFileWorkerListener(MvcModel model) { this.model = model; } @Override public void propertyChange(PropertyChangeEvent evt) { if (SwingWorker.StateValue.DONE == evt.getNewValue()) { OpenFileWorker openFileWorker = (OpenFileWorker) evt.getSource(); try { String text = openFileWorker.get(); model.setText(text); model.setStatus("File \"" + openFileWorker.getFile().getName() + "\" opened"); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } } @SuppressWarnings("serial") class FileAction extends AbstractAction { public FileAction(String name, int keyCode) { super(name); putValue(MNEMONIC_KEY, keyCode); } @Override public void actionPerformsd(ActionEvent arg0) { // pretty much empty } } @SuppressWarnings("serial") class SaveToFileAction extends AbstractAction { private MvcView view; private MvcModel model; public SaveToFileAction(MvcView view, MvcModel model, String name, int keyCode) { super(name); putValue(MNEMONIC_KEY, keyCode); this.view = view; this.model = model; } @Override public void actionPerformsd(ActionEvent e) { // TODO finish! } } @SuppressWarnings("serial") class ExitAction extends AbstractAction { private MvcView view; // private MvcModel model; // TODO: may use this later public ExitAction(MvcView view, MvcModel model, String name, int keyCode) { super(name); putValue(MNEMONIC_KEY, keyCode); this.view = view; // this.model = model; // TODO: may use this later } @Override public void actionPerformsd(ActionEvent e) { view.getTopWindow().dispose(); } } class ModelListener implements PropertyChangeListener { private MvcView view; private MvcModel model; public ModelListener(MvcView view, MvcModel model) { this.view = view; this.model = model; } @Override public void propertyChange(PropertyChangeEvent pcEvt) { if (MvcModel.TEXT.equals(pcEvt.getPropertyName())) { view.setTextAreaText(model.getText()); } else if (MvcModel.STATUS.equals(pcEvt.getPropertyName())) { view.setStatusText(model.getStatus()); } } } 

In questo esempio, ho combinato le classi java in un file per brevità, ma nell’applicazione, sarebbero nei propri file ma condivideranno tutti lo stesso pacchetto. Si noti che sebbene questa possa essere “over-kill” per questa semplice applicazione, ho usato questo tipo di struttura con diverse applicazioni Swing molto grandi con un buon successo. Il principale vantaggio per me arriva quando ho bisogno di eseguire il debug o migliorare un programma mesi dopo la creazione, poiché questa separazione di preoccupazioni e informazioni e comportamenti rende molto più facile per me apportare modifiche in una parte del programma senza offendere o sconvolgere un’altra parte di il programma.

Inoltre, la ragione delle interfacce è che puoi creare GUI nuove o diverse che hanno un aspetto diverso, ma che possono rispondere allo stesso modo. Li ho anche usati frequentemente per aiutare a creare classi di simulazione che mi permettessero di testare meglio i miei moduli in isolamento.

Consiglierei l’uso di azioni da utilizzare lì. Su ogni azione hai qualche specifico actionlistener, imposta il pannello come ascoltatore se ti piace farlo:

Azione spiegata

Per approfondire ciò che ha detto Kitesurfer, userei un’azione perché molto spesso ho pulsanti della barra degli strumenti o altri componenti che eseguono le stesse azioni delle voci di menu. Per evitare il codice duplicato o non necessario da qualche parte nella class (o qualsiasi altro in cui sono in grado di accedervi dalla class corrente) creo un campo Action che posso riutilizzare se necessario o spostare se decido di rifattorizzare il mio codice. Ecco un esempio.

 JMenuItem miLoad = new JMenuItem(actionLoad); Action actionLoad = new AbstractAction("Load") { public void actionPerformsd(ActionEvent e) { //your code to load the file goes here like a normal ActionListener } }; 

Verifica definitivamente l’API per vedere quali parametri possono essere passati nella class AbstractAction , ho usato una String modo che JMenuItem possa visualizzare la stringa, puoi anche impostare l’ Icon , non ricordo tutti i costruttori quindi varrebbe la pena dare un’occhiata Oh e passare JMenuItems nel costruttore della class DrawingPanel non è necessariamente una ctriggers idea, ma se eredita da JPanel non credo che tu possa aggiungere una barra dei menu a un JPanel quindi assicurati che venga aggiunto anche al tuo JFrame . Spero possa aiutare.

La soluzione rapida per il tuo problema è di fornire un gestore in modo che tu possa colbind ActionListener. Tu fai il JMenus in main() , ma non hai visibilità su DrawingPanel . Un modo economico per ottenere questo risultato sarebbe quello di passare JMenuItems nel costruttore DrawingPanel. Modifica il costruttore in qualcosa di simile:

 DrawingPanel(JMenuItem save, JMenuItem open) { // the stuff you already got this.save = save; this.open = open; save.addActionListener(this); open.addActionListener(this); } 

Poi li passeresti nel tuo DrawingPanel in main :

 JMenuItem loadMenuItem = new JMenuItem("Load"); JMenuItem saveMenuItem = new JMenuItem("Save"); ... frame.getContentPane(new DrawingPanel(saveMenuItem, loadMenuItem)); 

Da una prospettiva in stile Java, non è chiaro se DrawingPanel sia il posto migliore per gestire le azioni di salvataggio e caricamento. Come altri hanno menzionato, la creazione di azioni (separate) per ogni JMenuItem è spesso un modello migliore. Nel tuo caso, potresti essere in grado di fornire altri metodi di accesso o di aiuto all’interno di DrawingPanel che darebbero ai lavoratori di saver / loader le informazioni di cui avrebbero bisogno per eseguire la loro azione.


Modifica: (per indirizzare il “nuovo codice” dell’OP Modifica)

Penso che il problema con il “nuovo codice” sia che stai facendo un new JFileChooser nel metodo actionPerformsd , e non usando quello esistente che hai creato in precedenza e aggiunto al frame. Se si effettua la prima final è ansible utilizzare lo stesso JFileChooser nel metodo actionPerformsd .