2012-05-04 11 views
5

He creado algunas aplicaciones de GUI en Java anteriormente, y cada vez que estoy realmente haciendo el tonto hasta que la cosa hace lo que quiero que haga, usando snippits de varios ejemplos de GUI que encuentro en la web. Recientemente, leí un poco más sobre buenas prácticas cuando escribo una GUI en Java usando Swing, pero aún hay cosas que no me quedan claras. Permítanme primero describir lo que quiero hacer en mi próximo proyecto para que podamos usar eso como un ejemplo:Dónde almacenar qué en un modelo MVC y cómo se comunican los componentes?

Me gustaría crear una aplicación ejecutable que le da al usuario la capacidad de cargar imágenes, realizar algunas acciones en estas imágenes y guarda el proyecto. Debería haber un visor de imágenes principal en el núcleo con un navegador simple debajo de él para alternar entre las imágenes cargadas. Debería haber paneles con botones que realizan operaciones en una imagen (por ejemplo, un botón 'seleccionar el color de fondo de la imagen') y estas operaciones deberían ser accesibles desde una barra de herramientas o un menú.

sé que, por ejemplo, la interfaz gráfica de usuario debe iniciarse desde el hilo de eventos de Despacho y que puede utilizar un SwingWorker para operaciones laboriosas. También aprendí que al usar Acciones puedo separar la funcionalidad del estado y crear una Acción para ambos, un botón del panel, un botón de la barra de herramientas y un elemento del menú.

Lo que no entiendo es cómo se comunican todas estas cosas entre sí, y donde pongo lo. Por ejemplo: ¿mantengo el estado de mi programa (por lo tanto, qué imagen se muestra actualmente, qué configuraciones están configuradas) en un modelo separado y pongo una referencia a este modelo en mi clase de ventana principal? ¿Y los controladores? ¿Guardo una referencia a los modelos en los controladores están bien? Y cuando hice algunos cálculos en una imagen, ¿actualizo la imagen en la interfaz gráfica de usuario desde el controlador, o desde la interfaz gráfica de usuario utilizando un simple repintado?

supongo que el principal problema que tengo es que no entiendo muy bien cómo hacer que las diferentes partes de mi programa se comunican. Una GUI consta de muchas partes, y luego están todas estas Acciones y Oyentes, modelos, controladores, y todas estas necesitan interactuar de alguna manera. Sigo agregando referencias a casi todo en todos estos objetos, pero hace que todo sea extremadamente desordenado.

Otro ejemplo que encontré en la web: http://www.devdaily.com/java/java-action-abstractaction-actionlistener

entiendo cómo funciona, lo que no entiendo es cómo cambiar la realidad "habría hecho la acción 'Cut'." en la acción de corte real. Digamos que implica cortar una parte de mi imagen en el visor, ¿habría pasado la imagen a la acción? Y si este proceso llevara mucho tiempo, ¿habría creado un nuevo SwingWorker dentro de la acción? Y entonces, ¿cómo dejaría que SwingWorker actualice la GUI mientras se calcula? ¿Pasaría una referencia de la GUI al SwingWorker de modo que pueda actualizarlo de vez en cuando?

¿Alguien tiene un buen ejemplo de cómo hacer esto o tal vez algunos consejos sobre cómo aprender correctamente esto, porque estoy en un poco de una pérdida. Hay tanta información y tantas formas diferentes de hacer las cosas, y realmente quiero aprender a crear aplicaciones escalables con código limpio. ¿Existe tal vez un buen proyecto de código abierto sin demasiado código que demuestre muy bien una GUI como la que describí para que pueda aprender de eso?

+1

Ver también esta [Ejemplo] (http://stackoverflow.com/a/3072979/230513) o esta [Ejemplo] (http://stackoverflow.com/a/8534162/230513), ya sea de que puede informar a su [sscce] (http://sscce.org/). – trashgod

+0

Es posible que también desee comprobar MVP (Model-View-Presenter), p. en [esta presentación] (http://www.jgoodies.com/download/presentations/patterns-and-binding.pdf) – Robin

Respuesta

6

He creado algunas GUI de Swing y SWT. Lo que he encontrado es que una GUI requiere su propia estructura de modelo de vista (MV). El controlador de la aplicación interactúa con el modelo de GUI, en lugar de los componentes de la GUI.

Básicamente, construyo un grano de Java para cada JPanel de Swing en mi interfaz gráfica de usuario. Los componentes de la GUI interactúan con el bean Java, y el controlador de la aplicación interactúa con los beans de Java.

Aquí hay una interfaz gráfica de usuario Espirógrafo que creé.

enter image description here

Aquí está el bean Java que gestiona los parámetros 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; 
    } 

} 

Editado para agregar la clase del Panel de control.

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, esto parece muy útil, así que supongo que tendré que guardar el estado del programa , la parte de la GUI al menos, en algún objeto MainWindowState, por ejemplo. Pero luego mi siguiente pregunta: ¿qué sucede cuando alguien hace clic en el botón de color de la pluma? ¿Hay un oyente conectado a eso que tenga acceso al Modelo de Control? ¿Y este oyente está en una clase separada o está agregado al Control JPanel como una clase interna? – FinalArt2005

+1

La forma más fácil de responder a su pregunta es incluir el código del Panel de control en mi respuesta. –

+1

Muchas gracias por mostrarme el código de su aplicación GUI, realmente me ayudó a entender cómo interactúan los diferentes componentes en su aplicación. He escrito un plan de diseño para mi aplicación ahora y comenzaré a escribir código mañana. Si tengo problemas, volveré aquí, pero por ahora me ayudaste mucho, ¡gracias! – FinalArt2005

Cuestiones relacionadas