重写到MVC后GUI无法正常工作 [英] GUI not working after rewriting to MVC
问题描述
我正在练习MVC风格的编程。我在一个文件中有一个Mastermind游戏,工作正常(可能除了Check按钮在开始时不可见)。
I'm practicing MVC style programming. I have a Mastermind game in a single file, working fine (maybe apart of the fact that "Check" button is invisible at start).
http://paste.pocoo.org/show/226726/
但是当我把它重写为模型,视图,控制器文件时 - 当我点击空Pin(应该更新,并重新绘制新颜色)时 - 注意到了。谁能在这里看到任何问题?我尝试在不同的地方放置repaint(),但它根本不起作用:/
But when I've rewritten it to model, view, controller files - and when I click on empty Pin (that should be updated, and repainted with new color) - noting happens. Can anybody see any problems here ? I've tried placing repaint() in different places, but it simply does not work at all :/
主要:
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);
}
}
型号:
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;
}
}
查看:
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 );
}
}
控制器:
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) {}
}
推荐答案
正如您所发现的,模型 - 视图 - 控制器模式不是灵丹妙药,但它提供了一些优势。根据 MVC ,Swing可分离模型架构在 Swing架构概述 。根据此大纲,以下示例显示 MVC 实现了一个更简单的游戏,说明了类似的原则。请注意,模型
管理随机选择的单个 Piece
。为了响应用户的选择, View
调用 check()
方法,同时侦听来自<的响应code>型号通过 update()
。 视图
然后使用从模型
获得的信息自行更新。同样,控制器
可能 reset()
模型
。特别是, Model
中没有绘图, View
中没有游戏逻辑。这个稍微复杂的游戏旨在说明相同的概念。
As you've discovered, the Model–View–Controller pattern is no panacea, but it offers some advantages. Rooted in MVC, the Swing separable model architecture is discussed in A Swing Architecture Overview. Based on this outline, the following example shows an MVC implementation of a much simpler game that illustrates similar principles. Note that the Model
manages a single Piece
, chosen at random. In response to a user's selection, the View
invokes the check()
method, while listening for a response from the Model
via update()
. The View
then updates itself using information obtained from the Model
. Similarly, the Controller
may reset()
the Model
. In particular, there is no drawing in the Model
and no game logic in the View
. This somewhat more complex game was designed to illustrate the same concepts.
附录:我修改了原始示例以显示如何 MVC 允许人们在不改变模型
的性质的情况下增强查看
。
Addendum: I've modified the original example to show how MVC allows one to enhance the View
without changing the nature of the Model
.
附录:正如@akf观察到的, MVC 取决于< a href =http://en.wikipedia.org/wiki/Observer_pattern =noreferrer>观察者模式。您的模型
需要一种方法来通知查看
的更改。有几种方法被广泛使用:
Addendum: As @akf observes, MVC hinges on the observer pattern. Your Model
needs a way to notify the View
of changes. Several approaches are widely used:
-
在下面的示例中,
模型
扩展Observable
为简单起见。
In the example below,
Model
extendsObservable
for simplicity.
更常见的方法是使用 EventListenerList
,如 转换器
应用程序和建议者大量的 EventListener
子接口和实现类。
A more common approach uses an EventListenerList
, as shown in the Converter
application and suggested by the large number of EventListener
subinterfaces and implementing classes.
第三种选择是使用 PropertyChangeListener
,如sh拥有此处和此处 。
A third option is to use a PropertyChangeListener
, as shown here and here.
Addendum: Some common questions about Swing controllers are addressed here and here.
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;
}
}
这篇关于重写到MVC后GUI无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!