2010-06-18 12 views
117

Mi sto esercitando nella programmazione in stile MVC. Ho un gioco Mastermind in un singolo file, che funziona bene (forse a parte il fatto che il pulsante "Verifica" è invisibile all'inizio).GUI non funziona dopo la riscrittura in MVC

http://paste.pocoo.org/show/226726/

Ma quando ho riscritto per modellare, visualizzare i file del controller - e quando clicco su vuoto Pin (che dovrebbe essere aggiornato, e ridipinto con nuovo colore) - notando accade. Qualcuno può vedere qualche problema qui? Ho provato mettendo ridipingere() in luoghi diversi, ma semplicemente non funziona affatto:/

principale:

public class Main { 
    public static void main(String[] args){ 
     Model model = new Model(); 
     View view = new View("Mastermind", 400, 590, model); 
     Controller controller = new Controller(model, view); 
     view.setVisible(true); 
    } 
} 

Modello:

import java.util.Random; 

public class Model{ 
    static final int 
    LINE = 5, 
    SCORE = 10, OPTIONS = 20; 
    Pin pins[][] = new Pin[21][LINE]; 
    int combination[] = new int[LINE]; 
    int curPin = 0; 
    int turn = 1; 
    Random generator = new Random(); 
    int repaintPin; 
    boolean pinsRepaint=false; 
    int pinsToRepaint; 
    boolean isUpdate = true, isPlaying = true, isRowFull = false; 
    static final int HIT_X[] = {270,290,310,290,310}, HIT_Y[] = {506,496,496,516,516}; 

    public Model(){ 

     for (int i=0; i < SCORE; i++){ 
      for (int j = 0; j < LINE; j++){ 
       pins[i][j] = new Pin(20,0); 
       pins[i][j].setPosition(j*50+30,510-i*50); 
       pins[i+SCORE][j] = new Pin(8,0); 
       pins[i+SCORE][j].setPosition(HIT_X[j],HIT_Y[j]-i*50); 
      } 
     } 
     for (int i=0; i < LINE; i++){ 
      pins[OPTIONS][i] = new Pin(20, i+2); 
      pins[OPTIONS][i].setPosition(370,i * 50 + 56); 
     } 

    } 

    void fillHole(int color) { 
     pins[turn-1][curPin].setColor(color+1); 
     pinsRepaint = true; 
     pinsToRepaint = turn; 
     curPin = (curPin+1) % LINE; 
     if (curPin == 0){ 
      isRowFull = true; 
     } 
     pinsRepaint = false; 
     pinsToRepaint = 0; 
    } 

    void check() { 
     int junkPins[] = new int[LINE], junkCode[] = new int[LINE]; 
     int pinCount = 0, pico = 0; 

     for (int i = 0; i < LINE; i++) { 
      junkPins[i] = pins[turn-1][i].getColor(); 
      junkCode[i] = combination[i]; 
     } 
     for (int i = 0; i < LINE; i++){ 
      if (junkPins[i]==junkCode[i]) { 
       pins[turn+SCORE][pinCount].setColor(1); 
       pinCount++; 
       pico++; 
       junkPins[i] = 98; 
       junkCode[i] = 99; 
      } 
     } 
     for (int i = 0; i < LINE; i++){ 
      for (int j = 0; j < LINE; j++) 
       if (junkPins[i]==junkCode[j]) { 
        pins[turn+SCORE][pinCount].setColor(2); 
        pinCount++; 
        junkPins[i] = 98; 
        junkCode[j] = 99; 
        j = LINE; 
      } 
     } 
     pinsRepaint = true; 
     pinsToRepaint = turn + SCORE; 
     pinsRepaint = false; 
     pinsToRepaint=0; 

     if (pico == LINE){ 
      isPlaying = false; 
     } 
     else if (turn >= 10){ 
       isPlaying = false; 
     } 
     else{ 
      curPin = 0; 
      isRowFull = false; 
      turn++; 
     } 
    } 

    void combination() { 
     for (int i = 0; i < LINE; i++){ 
      combination[i] = generator.nextInt(6) + 1; 
     } 
    } 
} 

class Pin{ 
    private int color, X, Y, radius; 

    public Pin(){ 
     X = 0; Y = 0; radius = 0; color = 0; 
    } 

    public Pin(int r,int c){ 
     X = 0; Y = 0; radius = r; color = c; 
    } 

    public int getX(){ 
     return X; 
    } 

    public int getY(){ 
     return Y; 
    } 

    public int getRadius(){ 
     return radius; 
    } 

    public void setRadius(int r){ 
     radius = r; 
    } 

    public void setPosition(int x,int y){ 
     this.X = x ; 
     this.Y = y ; 
    } 
    public void setColor(int c){ 
     color = c; 
    } 
    public int getColor() { 
     return color; 
    } 
} 

Vista:

import java.awt.*; 
import javax.swing.*; 

public class View extends Frame{ 
    Model model; 
    JButton checkAnswer; 
    private JPanel button; 
    private static final Color COLORS[] = {Color.black, Color.white, Color.red, Color.yellow, Color.green, Color.blue, new Color(7, 254, 250)}; 

    public View(String name, int w, int h, Model m){ 
     model = m; 
     setTitle(name); 
     setSize(w,h); 
     setResizable(false); 
     this.setLayout(new BorderLayout()); 

     button = new JPanel(); 
     button.setSize(new Dimension(400, 100)); 
     button.setVisible(true); 
     checkAnswer = new JButton("Check"); 
     checkAnswer.setSize(new Dimension(200, 30)); 
     button.add(checkAnswer); 
     this.add(button, BorderLayout.SOUTH); 
     button.setVisible(true); 
    } 

    @Override 
    public void paint(Graphics g) { 
     g.setColor(new Color(238, 238, 238)); 
     g.fillRect(0,0,400,590); 

     for (int i=0; i < model.pins.length; i++) { 
      paintPins(model.pins[i][0],g); 
      paintPins(model.pins[i][1],g); 
      paintPins(model.pins[i][2],g); 
      paintPins(model.pins[i][3],g); 
      paintPins(model.pins[i][4],g); 
     } 
    } 

    @Override 
    public void update(Graphics g) { 
     if (model.isUpdate) { 
      paint(g); 
     } 
     else { 
      model.isUpdate = true; 
      paintPins(model.pins[model.repaintPin-1][0],g); 
      paintPins(model.pins[model.repaintPin-1][1],g); 
      paintPins(model.pins[model.repaintPin-1][2],g); 
      paintPins(model.pins[model.repaintPin-1][3],g); 
      paintPins(model.pins[model.repaintPin-1][4],g); 
     } 
    } 

    void repaintPins(int pin) { 
     model.repaintPin = pin; 
     model.isUpdate = false; 
     repaint(); 
    } 

    public void paintPins(Pin p, Graphics g){ 
     int X = p.getX(); 
     int Y = p.getY(); 
     int color = p.getColor(); 
     int radius = p.getRadius(); 
     int x = X-radius; 
     int y = Y-radius; 

     if (color > 0){ 
      g.setColor(COLORS[color]); 
      g.fillOval(x,y,2*radius,2*radius); 
     } 
     else{ 
      g.setColor(new Color(238, 238, 238)); 
      g.drawOval(x,y,2*radius-1,2*radius-1); 
     } 
     g.setColor(Color.black); 
     g.drawOval(x,y,2*radius,2*radius); 
    } 
} 

Controller:

import java.awt.*; 
import java.awt.event.*; 

public class Controller implements MouseListener, ActionListener { 
    private Model model; 
    private View view; 

    public Controller(Model m, View v){ 
     model = m; 
     view = v; 

     view.addWindowListener(new WindowAdapter(){ 
      public void windowClosing(WindowEvent e){ 
      System.exit(0); 
     } }); 
     view.addMouseListener(this); 
     view.checkAnswer.addActionListener(this); 
     model.combination(); 
    } 

    public void actionPerformed(ActionEvent e) { 
     if(e.getSource() == view.checkAnswer){ 
      if(model.isRowFull){ 
       model.check(); 
      } 
     } 
    } 

    public void mousePressed(MouseEvent e) { 
     Point mouse = new Point(); 

     mouse = e.getPoint(); 
     if (model.isPlaying){ 
      if (mouse.x > 350) { 
       int button = 1 + (int)((mouse.y - 32)/50); 
       if ((button >= 1) && (button <= 5)){ 
        model.fillHole(button); 
        if(model.pinsRepaint){ 
         view.repaintPins(model.pinsToRepaint); 
        } 
       } 
      } 
     } 
    } 

    public void mouseClicked(MouseEvent e) {} 
    public void mouseReleased(MouseEvent e){} 
    public void mouseEntered(MouseEvent e) {} 
    public void mouseExited(MouseEvent e) {} 
} 
+5

Sia il vecchio che il nuovo codice hanno problemi relativi alla combinazione di componenti AWT e Swing. Vedi anche http://stackoverflow.com/questions/2687871 – trashgod

+0

quindi il problema senza aggiornamento potrebbe essere causato da questo? –

+0

Sì. http://java.sun.com/products/jfc/tsc/articles/mixing/ – trashgod

risposta

142

Come hai scoperto, il modello Model–View–Controller non è una panacea, ma offre alcuni vantaggi. Radicato in MVC, l'architettura del modello separabile Swing è discussa in A Swing Architecture Overview. Basato su this outline, l'esempio seguente mostra un'implementazione di MVC di un gioco molto più semplice che illustra principi simili. Si noti che il gestisce un singolo Piece, scelto a caso. In risposta alla selezione di un utente, il View invoca il metodo check(), durante l'ascolto di una risposta da tramite update(). Lo View si aggiorna automaticamente utilizzando le informazioni ottenute dallo Model. Allo stesso modo, Controller può reset() il Model. In particolare, non vi è alcun disegno nello e nessuna logica di gioco nello View. Questo un po 'più complesso game è stato progettato per illustrare gli stessi concetti.

Addendum: ho modificato l'esempio originale per mostrare come MVC consente di migliorare lo View senza modificare la natura dello Model.

Addendum: come osserva @akf, MVC cardini su observer pattern. Il tuo Model necessita di un modo per notificare lo View di modifiche. Diversi approcci sono ampiamente utilizzati:

Addendum: Alcune domande comuni su controller di Swing sono affrontati here e here.

screen capture

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Observable; 
import java.util.Observer; 
import java.util.Random; 
import javax.swing.Icon; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 

/** 
* @see https://stackoverflow.com/q/3066590/230513 
* 15-Mar-2011 r8 https://stackoverflow.com/questions/5274962 
* 26-Mar-2013 r17 per comment 
*/ 
public class MVCGame implements Runnable { 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new MVCGame()); 
    } 

    @Override 
    public void run() { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(new MainPanel()); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 
} 

class MainPanel extends JPanel { 

    public MainPanel() { 
     super(new BorderLayout()); 
     Model model = new Model(); 
     View view = new View(model); 
     Control control = new Control(model, view); 
     JLabel label = new JLabel("Guess what color!", JLabel.CENTER); 
     this.add(label, BorderLayout.NORTH); 
     this.add(view, BorderLayout.CENTER); 
     this.add(control, BorderLayout.SOUTH); 
    } 
} 

/** 
* Control panel 
*/ 
class Control extends JPanel { 

    private Model model; 
    private View view; 
    private JButton reset = new JButton("Reset"); 

    public Control(Model model, View view) { 
     this.model = model; 
     this.view = view; 
     this.add(reset); 
     reset.addActionListener(new ButtonHandler()); 
    } 

    private class ButtonHandler implements ActionListener { 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      String cmd = e.getActionCommand(); 
      if ("Reset".equals(cmd)) { 
       model.reset(); 
      } 
     } 
    } 
} 

/** 
* View 
*/ 
class View extends JPanel { 

    private static final String s = "Click a button."; 
    private Model model; 
    private ColorIcon icon = new ColorIcon(80, Color.gray); 
    private JLabel label = new JLabel(s, icon, JLabel.CENTER); 

    public View(Model model) { 
     super(new BorderLayout()); 
     this.model = model; 
     label.setVerticalTextPosition(JLabel.BOTTOM); 
     label.setHorizontalTextPosition(JLabel.CENTER); 
     this.add(label, BorderLayout.CENTER); 
     this.add(genButtonPanel(), BorderLayout.SOUTH); 
     model.addObserver(new ModelObserver()); 
    } 

    private JPanel genButtonPanel() { 
     JPanel panel = new JPanel(); 
     for (Piece p : Piece.values()) { 
      PieceButton pb = new PieceButton(p); 
      pb.addActionListener(new ButtonHandler()); 
      panel.add(pb); 
     } 
     return panel; 
    } 

    private class ModelObserver implements Observer { 

     @Override 
     public void update(Observable o, Object arg) { 
      if (arg == null) { 
       label.setText(s); 
       icon.color = Color.gray; 
      } else { 
       if ((Boolean) arg) { 
        label.setText("Win!"); 
       } else { 
        label.setText("Keep trying."); 
       } 
      } 
     } 
    } 

    private class ButtonHandler implements ActionListener { 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      PieceButton pb = (PieceButton) e.getSource(); 
      icon.color = pb.piece.color; 
      label.repaint(); 
      model.check(pb.piece); 
     } 
    } 

    private static class PieceButton extends JButton { 

     Piece piece; 

     public PieceButton(Piece piece) { 
      this.piece = piece; 
      this.setIcon(new ColorIcon(16, piece.color)); 
     } 
    } 

    private static class ColorIcon implements Icon { 

     private int size; 
     private Color color; 

     public ColorIcon(int size, Color color) { 
      this.size = size; 
      this.color = color; 
     } 

     @Override 
     public void paintIcon(Component c, Graphics g, int x, int y) { 
      Graphics2D g2d = (Graphics2D) g; 
      g2d.setRenderingHint(
       RenderingHints.KEY_ANTIALIASING, 
       RenderingHints.VALUE_ANTIALIAS_ON); 
      g2d.setColor(color); 
      g2d.fillOval(x, y, size, size); 
     } 

     @Override 
     public int getIconWidth() { 
      return size; 
     } 

     @Override 
     public int getIconHeight() { 
      return size; 
     } 
    } 
} 

/** 
* Model 
*/ 
class Model extends Observable { 

    private static final Random rnd = new Random(); 
    private static final Piece[] pieces = Piece.values(); 
    private Piece hidden = init(); 

    private Piece init() { 
     return pieces[rnd.nextInt(pieces.length)]; 
    } 

    public void reset() { 
     hidden = init(); 
     setChanged(); 
     notifyObservers(); 
    } 

    public void check(Piece guess) { 
     setChanged(); 
     notifyObservers(guess.equals(hidden)); 
    } 
} 

enum Piece { 

    Red(Color.red), Green(Color.green), Blue(Color.blue); 
    public Color color; 

    private Piece(Color color) { 
     this.color = color; 
    } 
} 
+1

@trevor_nise: ho aggiornato l'esempio sopra. Potresti trovare utile confrontare le revisioni. – trashgod

+0

+1 Mi chiedo come è possibile combinare MVC con n-tier. –

+2

Per chiunque sia curioso, Fowler ha pubblicato il seguente articolo nel 2006: http://martinfowler.com/eaaDev/SeparatedPresentation.html –

19

Quando si cerca attraverso swing, in un modo che i progettisti costantemente impiegano aggiornamento dei componenti View nella sua attuazione MVC è attraverso Observer/callback osservabile. Un esempio può essere visto nel AbstractTableModel, che ha una varietà di metodi fireTable*Changed/Updated/etc che avviserà tutti i suoi osservatori TableModelListener di mod sul modello.

Un'opzione è aggiungere un tipo listener alla classe Model e quindi notificare gli osservatori registrati di qualsiasi mod allo stato del modello. Il tuo View dovrebbe essere un listener e dovrebbe ridisegnarsi al ricevimento di un aggiornamento.

MODIFICA: +1 a trashgod. considera questo una formulazione alternativa alla sua spiegazione.

Problemi correlati