134 votes

GUI ne fonctionne ne pas après l’avoir réécrit pour MVC

Je suis pratiquant MVC style de programmation. J'ai un jeu Mastermind dans un seul fichier, fonctionne très bien (peut-être en dehors du fait que le bouton "rechercher" est invisible au début).

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

Mais quand j'ai réécrit pour modèle, vue, contrôleur de fichiers - et quand je clique sur la Broche vide (qui devrait être mis à jour, et repeint avec de nouvelles couleurs) - noter arrive. Tout le monde peut voir les problèmes ici ? J'ai essayé de placer repaint() dans différents endroits, mais il ne fonctionne tout simplement pas du tout :/

Principales :

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);
    }
}

Modèle :

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;
    }
}

Vue:

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 );
    }
}

Contrôleur:

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)  {}
}

160voto

trashgod Points 136305

Comme vous l'avez découvert, le Modèle–Vue–Contrôleur de schéma n'est pas la panacée, mais elle offre certains avantages. Enraciné dans MVC, le Swing séparables architecture du modèle est discuté dans Un Swing Présentation de l'Architecture. Basé sur ce plan, l'exemple suivant montre un MVC mise en œuvre d'un beaucoup plus simple de jeu qui illustre des principes similaires. Notez que l' Model gère un seul Piece, choisis au hasard. En réponse à une sélection de l'utilisateur, l' View invoque l' check() méthode, tout en écoutant une réponse de l' Model par update(). L' View puis se met à jour en utilisant l'information obtenue à partir de l' Model. De même, l' Controller peut reset() le Model. En particulier, il n'y a pas de dessin dans l' Model et aucun jeu de logique dans l' View. Un peu plus complexe jeu a été conçu pour illustrer les mêmes concepts.

Addendum: j'ai modifié l'original exemple pour montrer comment MVC permet d'améliorer l' View sans changer la nature de l' Model.

Addendum: Comme @akf observe, MVC charnières sur le pattern observer. Votre Model besoin d'un moyen d'avertir l' View de changements. Plusieurs approches sont couramment utilisées:

  • Dans l'exemple ci-dessous, Model s'étend Observable pour des raisons de simplicité.

  • Une approche plus commune utilise un EventListenerList, comme indiqué dans l' Converter application et suggéré par le grand nombre de EventListener des sous-interfaces et la mise en œuvre de classes.

  • Une troisième option consiste à utiliser un PropertyChangeListener, comme indiqué ici et ici.

Addendum: Quelques questions courantes concernant Swing contrôleurs sont abordés ici et ici.

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 http://stackoverflow.com/q/3066590/230513
 * 15-Mar-2011 r8 http://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;
    }
}

20voto

akf Points 23518

Lors de la recherche par le biais de Swing, d'une manière que les concepteurs d'employer systématiquement la mise à jour de la Vue des composants dans son MVC mise en œuvre est par le biais de l'Observateur/Observable rappels. Un exemple peut être vu dans l' AbstractTableModel, qui a une variété de fireTable*Changed/Updated/etc méthodes qui alerte l'ensemble de ses TableModelListener des observateurs de mods pour le modèle.

Une option que vous avez est d'ajouter un écouteur de type à votre Model classe, et puis en informer votre observateurs inscrits de tout les mods à l'état de votre modèle. Votre View doit être un auditeur, et il doit redessiner lui-même lors de la réception d'une mise à jour.

EDIT: +1 pour trashgod. considérez ceci une autre formulation de son explication.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X