2012-05-04 6 views
5

Ho creato alcune applicazioni GUI in Java prima e ogni volta mi preoccupo solo di fare ciò che voglio, usando gli snippits di diversi esempi di GUI che trovo sul web. Recentemente ho letto di più sulle buone pratiche durante la scrittura di una GUI in Java usando Swing, ma ancora alcune cose non sono chiare per me. Vorrei prima descrivere quello che voglio fare nel mio prossimo progetto in modo che possiamo utilizzarlo come esempio:Dove memorizzare cosa in un modello MVC e come comunicano i componenti?

Mi piacerebbe creare un'applicazione eseguibile che dia ad un utente la possibilità di caricare immagini, eseguire alcune azioni su queste immagini e salva il progetto. Ci dovrebbe essere un visualizzatore di immagini principale al centro con un semplice navigatore sotto di esso per passare tra le immagini caricate. Ci dovrebbero essere pannelli con pulsanti che eseguono operazioni su un'immagine (ad esempio un pulsante 'seleziona il colore di sfondo dall'immagine') e queste operazioni dovrebbero essere facoltativamente accessibili da una barra degli strumenti o da un menu.

So che ad esempio la GUI dovrebbe essere avviata dal thread di invio eventi e che posso utilizzare uno SwingWorker per le operazioni di time-tuning. Inoltre ho imparato che usando le azioni posso separare le funzionalità dallo stato e creare un'azione per un pulsante del pannello, un pulsante della barra degli strumenti e una voce di menu.

Quello che non capisco è come tutte queste cose comunicano tra loro e dove metto cosa. Ad esempio: mantengo lo stato del mio programma (quindi quale immagine è attualmente visualizzata, quali impostazioni sono impostate) in un modello separato e metto un riferimento a questo modello nella classe della mia finestra principale? E i controllori? Mantengo bene un riferimento ai modelli nei controller? E quando ho fatto qualche calcolo su un'immagine, aggiorno l'immagine nella GUI dal controller o dalla stessa GUI usando un semplice repaint?

Immagino che il problema principale che ho è che non capisco veramente come comunicare le diverse parti del mio programma. Una GUI è composta da molte parti, e poi ci sono tutte queste azioni e ascoltatori, modelli, controller e tutti questi hanno bisogno di interagire in qualche modo. Continuo ad aggiungere riferimenti a quasi tutto in tutti questi oggetti, ma rende tutto estremamente disordinato.

Un altro esempio che ho trovato sul web: http://www.devdaily.com/java/java-action-abstractaction-actionlistener

ho capito come funziona, cosa che non capisco è come cambiare la realtà "avrebbe fatto l'azione 'Cut'." nell'effettiva azione di taglio. Diciamo che implica il taglio di una parte dalla mia immagine nel visualizzatore, avrei passato l'immagine all'azione? E se questo processo dovesse richiedere molto tempo, avrei creato un nuovo SwingWorker all'interno dell'azione? E poi come farò a SwingWorker aggiornare la GUI durante il calcolo? Dovrei passare un riferimento della GUI a SwingWorker in modo che possa aggiornarlo di volta in volta?

Qualcuno ha un buon esempio su come fare questo o forse alcuni suggerimenti su come apprendere correttamente questo, perché sono un po 'una perdita. Ci sono così tante informazioni e tanti modi diversi di fare le cose, e voglio davvero imparare come creare applicazioni scalabili con codice pulito. C'è forse un buon progetto open-source con non troppo codice che dimostra molto bene una GUI come quello che ho descritto in modo che io possa imparare da quello?

+1

Vedi anche questo [esempio] (http://stackoverflow.com/a/3072979/230513) o questo [esempio] (http://stackoverflow.com/a/8534162/230513), entrambi che può informare il tuo [sscce] (http://sscce.org/). – trashgod

+0

Si potrebbe anche voler controllare MVP (Model-View-Presenter), ad es. in [questa presentazione] (http://www.jgoodies.com/download/presentations/patterns-and-binding.pdf) – Robin

risposta

6

Ho creato alcune GUI Swing e SWT. Quello che ho trovato è che una GUI richiede la propria struttura Model - View (MV). Il controller dell'applicazione interagisce con il modello della GUI, piuttosto che con i componenti della GUI.

Fondamentalmente, ho costruito un bean Java per ogni JPanel Swing nella mia GUI. I componenti della GUI interagiscono con il bean Java e il controller dell'applicazione interagisce con i bean Java.

Ecco una GUI Spirograph che ho creato.

enter image description here

Ecco il bean Java che gestisce i parametri Spirograph.

import java.awt.Color; 

public class ControlModel { 

    protected boolean isAnimated; 

    protected int jpanelWidth; 
    protected int outerCircle; 
    protected int innerCircle; 
    protected int penLocation; 
    protected int penSize; 

    protected Color penColor; 
    protected Color backgroundColor; 

    public ControlModel() { 
     init(); 
     this.penColor = Color.BLUE; 
     this.backgroundColor = Color.WHITE; 
    } 

    public void init() { 
     this.jpanelWidth = 512; 
     this.outerCircle = 1000; 
     this.innerCircle = 520; 
     this.penLocation = 400; 
     this.penSize = 2; 
     this.isAnimated = true; 
    } 

    public int getOuterCircle() { 
     return outerCircle; 
    } 

    public void setOuterCircle(int outerCircle) { 
     this.outerCircle = outerCircle; 
    } 

    public int getInnerCircle() { 
     return innerCircle; 
    } 

    public void setInnerCircle(int innerCircle) { 
     this.innerCircle = innerCircle; 
    } 

    public int getPenLocation() { 
     return penLocation; 
    } 

    public void setPenLocation(int penLocation) { 
     this.penLocation = penLocation; 
    } 

    public int getPenSize() { 
     return penSize; 
    } 

    public void setPenSize(int penSize) { 
     this.penSize = penSize; 
    } 

    public boolean isAnimated() { 
     return isAnimated; 
    } 

    public void setAnimated(boolean isAnimated) { 
     this.isAnimated = isAnimated; 
    } 

    public Color getPenColor() { 
     return penColor; 
    } 

    public void setPenColor(Color penColor) { 
     this.penColor = penColor; 
    } 

    public Color getBackgroundColor() { 
     return backgroundColor; 
    } 

    public void setBackgroundColor(Color backgroundColor) { 
     this.backgroundColor = backgroundColor; 
    } 

    public int getJpanelWidth() { 
     return jpanelWidth; 
    } 

    public int getJpanelHeight() { 
     return jpanelWidth; 
    } 

} 

Modificato per aggiungere la classe del Pannello di controllo.

import java.awt.Color; 
import java.awt.Component; 
import java.awt.Container; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.GridLayout; 
import java.awt.Insets; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

import javax.swing.JButton; 
import javax.swing.JColorChooser; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 
import javax.swing.JToggleButton; 
import javax.swing.SwingConstants; 

import com.ggl.spirograph.model.ControlModel; 

public class ControlPanel { 

    protected static final Insets entryInsets = new Insets(0, 10, 4, 10); 
    protected static final Insets titleInsets = new Insets(0, 10, 20, 10); 

    protected ControlModel model; 

    protected DrawingPanel drawingPanel; 

    protected JButton drawButton; 
    protected JButton stopButton; 
    protected JButton resetButton; 
    protected JButton foregroundColorButton; 
    protected JButton backgroundColorButton; 

    protected JLabel message; 

    protected JPanel panel; 

    protected JTextField outerCircleField; 
    protected JTextField innerCircleField; 
    protected JTextField penLocationField; 
    protected JTextField penSizeField; 
    protected JTextField penFadeField; 

    protected JToggleButton animationToggleButton; 
    protected JToggleButton fastToggleButton; 

    protected static final int messageLength = 100; 
    protected String blankMessage; 

    public ControlPanel(ControlModel model) { 
     this.model = model; 
     this.blankMessage = createBlankMessage(); 
     createPartControl(); 
     setFieldValues(); 
     setColorValues(); 
    } 

    public void setDrawingPanel(DrawingPanel drawingPanel) { 
     this.drawingPanel = drawingPanel; 
    } 

    protected String createBlankMessage() { 
     StringBuilder sb = new StringBuilder(); 
     for (int i = 0; i < messageLength/4; i++) { 
      sb.append(" "); 
     } 
     return sb.toString(); 
    } 

    protected void createPartControl() { 
     panel = new JPanel(); 
     panel.setLayout(new GridBagLayout()); 

     int gridy = 0; 

     JLabel title = new JLabel("Spirograph Parameters"); 
     title.setHorizontalAlignment(SwingConstants.CENTER); 
     addComponent(panel, title, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 

     resetButton = new JButton("Reset Default Parameters"); 
     resetButton.setHorizontalAlignment(SwingConstants.CENTER); 
     resetButton.addActionListener(new ResetButtonListener()); 
     addComponent(panel, resetButton, 0, gridy++, 4, 1, entryInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 

     JLabel outerCircleLabel = new JLabel("Outer circle radius:"); 
     outerCircleLabel.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, outerCircleLabel, 0, gridy, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     outerCircleField = new JTextField(5); 
     outerCircleField.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, outerCircleField, 2, gridy++, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     JLabel innerCircleLabel = new JLabel("Inner circle radius:"); 
     innerCircleLabel.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, innerCircleLabel, 0, gridy, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     innerCircleField = new JTextField(5); 
     innerCircleField.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, innerCircleField, 2, gridy++, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     JLabel penLocationLabel = new JLabel("Pen location radius:"); 
     penLocationLabel.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, penLocationLabel, 0, gridy, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     penLocationField = new JTextField(5); 
     penLocationField.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, penLocationField, 2, gridy++, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     JLabel penSizeLabel = new JLabel("Pen size:"); 
     penSizeLabel.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, penSizeLabel, 0, gridy, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     penSizeField = new JTextField(5); 
     penSizeField.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, penSizeField, 2, gridy++, 2, 1, entryInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     message = new JLabel(blankMessage); 
     message.setForeground(Color.RED); 
     message.setHorizontalAlignment(SwingConstants.LEFT); 
     addComponent(panel, message, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL); 

     title = new JLabel("Drawing Speed"); 
     title.setHorizontalAlignment(SwingConstants.CENTER); 
     addComponent(panel, title, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 

     JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 4, 0)); 

     animationToggleButton = new JToggleButton("Animated"); 
     animationToggleButton.setSelected(model.isAnimated()); 
     animationToggleButton.setHorizontalAlignment(SwingConstants.CENTER); 
     animationToggleButton.addActionListener(new DrawingSpeedListener(
       animationToggleButton)); 
     buttonPanel.add(animationToggleButton); 

     fastToggleButton = new JToggleButton("Fast"); 
     fastToggleButton.setSelected(!model.isAnimated()); 
     fastToggleButton.setHorizontalAlignment(SwingConstants.CENTER); 
     fastToggleButton.addActionListener(new DrawingSpeedListener(
       fastToggleButton)); 
     buttonPanel.add(fastToggleButton); 

     addComponent(panel, buttonPanel, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 

     title = new JLabel("Drawing Colors"); 
     title.setHorizontalAlignment(SwingConstants.CENTER); 
     addComponent(panel, title, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 

     buttonPanel = new JPanel(new GridLayout(1, 2, 4, 0)); 

     foregroundColorButton = new JButton("Pen"); 
     foregroundColorButton.setHorizontalAlignment(SwingConstants.CENTER); 
     foregroundColorButton.addActionListener(new ColorSelectListener(
       foregroundColorButton)); 
     buttonPanel.add(foregroundColorButton); 

     backgroundColorButton = new JButton("Paper"); 
     backgroundColorButton.setHorizontalAlignment(SwingConstants.CENTER); 
     backgroundColorButton.addActionListener(new ColorSelectListener(
       backgroundColorButton)); 
     buttonPanel.add(backgroundColorButton); 

     addComponent(panel, buttonPanel, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 

     title = new JLabel("Drawing Controls"); 
     title.setHorizontalAlignment(SwingConstants.CENTER); 
     addComponent(panel, title, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 

     buttonPanel = new JPanel(new GridLayout(1, 2, 4, 0)); 

     drawButton = new JButton("Draw"); 
     drawButton.setHorizontalAlignment(SwingConstants.CENTER); 
     drawButton.addActionListener(new DrawButtonListener()); 
     buttonPanel.add(drawButton); 

     stopButton = new JButton("Stop"); 
     stopButton.setHorizontalAlignment(SwingConstants.CENTER); 
     stopButton.addActionListener(new StopButtonListener()); 
     buttonPanel.add(stopButton); 

     addComponent(panel, buttonPanel, 0, gridy++, 4, 1, titleInsets, 
       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL); 
    } 

    protected void addComponent(Container container, Component component, 
      int gridx, int gridy, int gridwidth, int gridheight, 
      Insets insets, int anchor, int fill) { 
     GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, 
       gridwidth, gridheight, 1.0, 1.0, anchor, fill, insets, 0, 0); 
     container.add(component, gbc); 
    } 

    protected void setFieldValues() { 
     outerCircleField.setText(Integer.toString(model.getOuterCircle())); 
     innerCircleField.setText(Integer.toString(model.getInnerCircle())); 
     penLocationField.setText(Integer.toString(model.getPenLocation())); 
     penSizeField.setText(Integer.toString(model.getPenSize())); 
    } 

    protected void setColorValues() { 
     foregroundColorButton.setForeground(model.getBackgroundColor()); 
     foregroundColorButton.setBackground(model.getPenColor()); 

     backgroundColorButton.setForeground(model.getPenColor()); 
     backgroundColorButton.setBackground(model.getBackgroundColor()); 
    } 

    public JPanel getPanel() { 
     return panel; 
    } 

    public class ResetButtonListener implements ActionListener { 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      model.init(); 
      setFieldValues(); 
     } 

    } 

    public class StopButtonListener implements ActionListener { 

     @Override 
     public void actionPerformed(ActionEvent arg0) { 
      drawingPanel.stop(); 
     } 

    } 

    public class DrawButtonListener implements ActionListener { 

     @Override 
     public void actionPerformed(ActionEvent event) { 
      message.setText(blankMessage); 

      int ocTest = isNumeric(outerCircleField.getText()); 
      int icTest = isNumeric(innerCircleField.getText()); 
      int plTest = isNumeric(penLocationField.getText()); 
      int psTest = isNumeric(penSizeField.getText()); 

      boolean isInvalid = false; 

      if (psTest < 0) { 
       message.setText("Pen size is not a valid integer"); 
       isInvalid = true; 
      } 

      if (plTest < 0) { 
       message.setText("Pen location radius is not a valid integer"); 
       isInvalid = true; 
      } 

      if (icTest < 0) { 
       message.setText("Inner circle radius is not a valid integer"); 
       isInvalid = true; 
      } 

      if (ocTest < 0) { 
       message.setText("Outer circle radius is not a valid integer"); 
       isInvalid = true; 
      } 

      if (isInvalid) { 
       return; 
      } 

      if (ocTest > 1000) { 
       message.setText("The outer circle cannot be larger than 1000"); 
       isInvalid = true; 
      } 

      if (ocTest <= icTest) { 
       message.setText("The inner circle must be smaller than the outer circle"); 
       isInvalid = true; 
      } 

      if (icTest <= plTest) { 
       message.setText("The pen location must be smaller than the inner circle"); 
       isInvalid = true; 
      } 

      if (isInvalid) { 
       return; 
      } 

      model.setOuterCircle(ocTest); 
      model.setInnerCircle(icTest); 
      model.setPenLocation(plTest); 
      model.setPenSize(psTest); 

      drawingPanel.draw(model.isAnimated()); 
     } 

     protected int isNumeric(String field) { 
      try { 
       return Integer.parseInt(field); 
      } catch (NumberFormatException e) { 
       return Integer.MIN_VALUE; 
      } 
     } 

    } 

    public class DrawingSpeedListener implements ActionListener { 

     JToggleButton selectedButton; 

     public DrawingSpeedListener(JToggleButton selectedButton) { 
      this.selectedButton = selectedButton; 
     } 

     @Override 
     public void actionPerformed(ActionEvent arg0) { 
      if (selectedButton.equals(animationToggleButton)) { 
       model.setAnimated(true); 
      } else { 
       model.setAnimated(false); 
      } 

      animationToggleButton.setSelected(model.isAnimated()); 
      fastToggleButton.setSelected(!model.isAnimated()); 
     } 

    } 

    public class ColorSelectListener implements ActionListener { 

     JButton selectedButton; 

     public ColorSelectListener(JButton selectedButton) { 
      this.selectedButton = selectedButton; 
     } 

     @Override 
     public void actionPerformed(ActionEvent arg0) { 
      if (selectedButton.equals(foregroundColorButton)) { 
       Color initialColor = model.getPenColor(); 
       Color selectedColor = JColorChooser.showDialog(drawingPanel, 
         "Select pen color", initialColor); 
       if (selectedColor != null) { 
        model.setPenColor(selectedColor); 
       } 
      } else if (selectedButton.equals(backgroundColorButton)) { 
       Color initialColor = model.getBackgroundColor(); 
       Color selectedColor = JColorChooser.showDialog(drawingPanel, 
         "Select paper color", initialColor); 
       if (selectedColor != null) { 
        model.setBackgroundColor(selectedColor); 
       } 
      } 
      setColorValues(); 
     } 

    } 

} 
+0

Ok, questo sembra molto utile, quindi suppongo che dovrò salvare lo stato del programma , almeno la parte GUI di esso, in alcuni oggetti MainWindowState per esempio. Ma poi la mia prossima domanda: cosa succede quando qualcuno fa clic sul pulsante del colore della penna? C'è un ascoltatore collegato a ciò che ha accesso a ControlModel? Ed è questo ascoltatore in una classe separata o è aggiunto al Control JPanel come una classe interiore? – FinalArt2005

+1

Il modo più semplice per rispondere alla tua domanda è includere il codice per il Pannello di controllo nella mia risposta. –

+1

Grazie per avermi mostrato il codice della tua applicazione GUI, mi ha davvero aiutato a capire come interagiscono i diversi componenti della tua applicazione. Ho scritto un piano di progettazione per la mia applicazione ora e inizierò a scrivere il codice domani. Se incorrerò in problemi tornerò qui, ma per ora mi hai aiutato molto, grazie! – FinalArt2005

Problemi correlati