mvc example ejemplo java model-view-controller swing layout-manager jcomponent

example - mvc java swing



Es MVC en Swing Thread Safe (1)

Estoy tratando de tocar los límites de la arquitectura MVC en Swing, pero como probé todo, todos (desde SwingWorker o Runnable#Thread ) están hechos en EDT

Mis preguntas:

  • ¿Hay algunos límites o depende estrictamente por orden de las implementaciones ( SwingWorker en SwingWorker o Runnable#Thread )?

  • ¿Es limitado el método JComponent # Thread Safe o no?

  • característica esencial de una arquitectura MVC en Swing,?

  • Cª. Re-Layout del contenedor?

Nota: para mi SSCCE tomo uno de los mejores ejemplos de HFOE , y tal vez al mantener estos principios estrictamente no es posible crear ninguna falla de EDT o bloqueo de la GUI.

import java.awt.BorderLayout; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.LinkedList; import java.util.Queue; import javax.swing.*; public class MVC_ProgressBarThread { private MVC_ProgressBarThread() { MVC_View view = new MVC_View(); MVC_Model model = new MVC_Model(); MVC_Control control = new MVC_Control(view, model); view.setControl(control); JFrame frame = new JFrame("MVC_ProgressBarThread"); frame.getContentPane().add(view); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { MVC_ProgressBarThread mVC_ProgressBarThread = new MVC_ProgressBarThread(); } }); } } class MVC_View extends JPanel { private static final long serialVersionUID = 1L; private MVC_Control control; private JProgressBar progressBar = new JProgressBar(); private JButton startActionButton = new JButton("Press Me and Run this Madness"); private JLabel myLabel = new JLabel("Nothing Special"); public MVC_View() { startActionButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { buttonActionPerformed(); } }); JPanel buttonPanel = new JPanel(); startActionButton.setFocusPainted(false); buttonPanel.add(startActionButton); setLayout(new BorderLayout(10, 10)); add(buttonPanel, BorderLayout.NORTH); progressBar.setStringPainted(true); add(progressBar, BorderLayout.CENTER); myLabel.setIcon(UIManager.getIcon("OptionPane.questionIcon")); myLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); add(myLabel, BorderLayout.SOUTH); } public void setControl(MVC_Control control) { this.control = control; } private void buttonActionPerformed() { if (control != null) { control.doButtonAction(); } } public void setProgress(int progress) { progressBar.setValue(progress); } public void setProgressLabel(String label) { progressBar.setString(label); } public void setIconLabel(Icon icon) { myLabel.setIcon(icon); } public void start() { startActionButton.setEnabled(false); } public void done() { startActionButton.setEnabled(true); setProgress(100); setProgressLabel(" Done !!! "); setIconLabel(null); } } class MVC_Control { private MVC_View view; private MVC_Model model; public MVC_Control(final MVC_View view, final MVC_Model model) { this.view = view; this.model = model; model.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent pce) { if (MVC_Model.PROGRESS.equals(pce.getPropertyName())) { view.setProgress((Integer) pce.getNewValue()); } if (MVC_Model.PROGRESS1.equals(pce.getPropertyName())) { view.setProgressLabel((String) pce.getNewValue()); } if (MVC_Model.PROGRESS2.equals(pce.getPropertyName())) { view.setIconLabel((Icon) pce.getNewValue()); } } }); } public void doButtonAction() { view.start(); SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { model.reset(); model.startSearch(); return null; } @Override protected void done() { view.done(); } }; swingworker.execute(); } } class MVC_Model { public static final String PROGRESS = "progress"; public static final String PROGRESS1 = "progress1"; public static final String PROGRESS2 = "progress2"; private static final int MAX = 11; private static final long SLEEP_DELAY = 1000; private int progress = 0; private String label = "Start"; private PropertyChangeSupport pcs = new PropertyChangeSupport(this); private PropertyChangeSupport pcs1 = new PropertyChangeSupport(this); private PropertyChangeSupport pcs2 = new PropertyChangeSupport(this); private final String[] petStrings = {"Bird", "Cat", "Dog", "Rabbit", "Pig", "Fish", "Horse", "Cow", "Bee", "Skunk"}; private int index = 1; private Queue<Icon> iconQueue = new LinkedList<Icon>(); private Icon icon = (UIManager.getIcon("OptionPane.questionIcon")); public void setProgress(int progress) { int oldProgress = this.progress; this.progress = progress; PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS, oldProgress, progress); pcs.firePropertyChange(evt); } public void setProgressLabel(String label) { String oldString = this.label; this.label = label; PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS1, oldString, label); pcs1.firePropertyChange(evt); } public void setIconLabel(Icon icon) { Icon oldIcon = this.icon; this.icon = icon; PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS2, oldIcon, icon); pcs2.firePropertyChange(evt); } public void reset() { setProgress(0); } public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); pcs1.addPropertyChangeListener(listener); pcs2.addPropertyChangeListener(listener); } public void startSearch() { iconQueue.add(UIManager.getIcon("OptionPane.errorIcon")); iconQueue.add(UIManager.getIcon("OptionPane.informationIcon")); iconQueue.add(UIManager.getIcon("OptionPane.warningIcon")); iconQueue.add(UIManager.getIcon("OptionPane.questionIcon")); for (int i = 0; i < MAX; i++) { int newValue = (100 * i) / MAX; setProgress(newValue); setProgressLabel(petStrings[index]); index = (index + 1) % petStrings.length; setIconLabel(nextIcon()); try { Thread.sleep(SLEEP_DELAY); } catch (InterruptedException e) { } } } private Icon nextIcon() { Icon icon1 = iconQueue.peek(); iconQueue.add(iconQueue.remove()); return icon1; } }


Esto es demasiado largo para un comentario ...

Primero, y esto no está relacionado con el resto de esta respuesta: hay muchos MVC diferentes y el que usaste en ese fragmento de código que publicaste aquí no es el mismo que el utilizado en el artículo al que vinculaste: http: / /www.oracle.com/technetwork/articles/javase/mvc-136693.html

El artículo señala correctamente que es solo "una implementación común de MVC" (una en la que la vista registra a un oyente que escucha los cambios del modelo). Su implementación es un tipo diferente de MVC, donde el controlador registra a un oyente que escucha los cambios del modelo y luego actualiza la vista.

No es que haya nada de malo en eso: hay muchos tipos diferentes de MVC (*).

(Otra pequeña advertencia ... Su vista es consciente de su controlador en su ejemplo, que es un poco raro: hay otras maneras de hacer lo que está haciendo sin necesidad de "alimentar" el controlador a la vista como lo hace con su setControl (...) dentro de su MVCView ).

Pero de todos modos ... Básicamente, casi siempre estás modificando la GUI desde fuera del EDT (cosa que no deberías hacer):

public void setIconLabel(final Icon icon) { myLabel.setIcon(icon); }

Puede verificarlo agregando esto:

System.out.println("Are we on the EDT? " + SwingUtilities.isEventDispatchThread());

Esto se debe a que finalmente estás haciendo estas actualizaciones desde tu subproceso SwingWorker (el subproceso SwingWorker se ejecuta fuera del EDT: básicamente es el punto de un trabajador de Swing).

Preferiría actualizar la GUI del EDT, haciendo algo como esto:

public void setIconLabel(final Icon icon) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { myLabel.setIcon(icon); } }); }