Java MVC项目 - 要么我无法更新绘图,要么我看不到它。

4
我有一个基于MVC范例的项目,但是一直无法正常运行。
程序中有四个面板,这些面板应该允许我以不同的方式修改屏幕上绘制的椭圆形状。它们似乎能够很好地工作,经过一番努力,我成功将它们显示在包含整个程序的JFrame窗口中。但是,如果我按照提供的说明进行操作,我只能看到一个空的框架。而如果我自己编写代码,则无法更新椭圆形状。
这个项目有非常具体的说明,我也一直遵循指导,但部分文档并不清晰。我认为我缺少的必须是某些简单的东西,因为没有任何问题跳出来让我感到困惑。然而,我得承认我的Java经验有限,对GUI设计和范例也几乎没有接触过。
总之,我已经在网上和这个网站上广泛搜索,试图找出问题所在,但这是一个相当特殊的例子,老实说,我根本就不了解这方面的知识,也无法从网上找到通用的答案,并弄清楚缺失了什么。我已经花费了太长时间来查看此代码,所以非常希望有人能帮助我。
public class Model {
    private Controller controller;
    private View view;
    private MvcFrame mvcFrame;

    private int radius = 44;
    private Color color = Color.BLUE;
    private boolean solid = true;

    //bunch of mutators and accessors for the above variables

    public Model() {
        controller = new Controller(this);
        view = new View(this);
        mvcFrame = new MvcFrame(this);
    }
}

这是模型类。它看起来相当简单。我认为我的理解很扎实,没有任何错误。主要包含上下文信息。
public class Controller extends JPanel{
    private Model model;

    public Controller(Model model) {
        this.model = model;
        setBorder(BorderFactory.createLineBorder(Color.GREEN));
        setLayout(new GridLayout(4,1));
        add(new RadiusPanel(model));
        add(new ColorPanel(model));
        add(new SolidPanel(model));
        add(new TitlePanel(model));
    }
}

这是控制器类。据我所知,setBorder、setLayout和一系列的adds在这里没有作用。我把它们注释掉了,但这是指令告诉我要这样做的方式,所以要么有一个错误,要么是我的设置有问题。然而,当我这样做时,我会得到一个空窗口(JFrame),但其中没有任何面板显示出来。我为解决这个问题是将这些add函数放到mvcFrame类中:

public class MvcFrame extends JFrame {
    private Model model;

    public MvcFrame(Model model){
        this.model = model;
        //setLayout(new GridLayout(4,1));
        //add(new RadiusPanel(model));
        //add(new ColorPanel(model));
        //add(new SolidPanel(model));
        //add(new TitlePanel(model));

        //add(new View(model));


        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setSize(800,600);
        setVisible(true);
    }
}

所以,这里的事情开始变得有点奇怪。第一个被注释掉的代码块与Controller类中的代码相同。我将其注释掉的原因是因为那只是一个幸运的猜测——根据说明书,它不应该是那样的。然而,这确实可以让面板显示出来——但那时我仍然在拼命地想办法让椭圆形显示出来。
另一个被注释掉的行(add(new View(model));)是尝试使事情正常化的不同方法。在这种情况下,我将那些add函数放在了View类中(见下面的被注释掉的代码)。这实际上可以显示椭圆和面板,但这种方法不能让我更新椭圆形。此外,虽然我只是让椭圆形显示出来,但我似乎无法弄清楚到底是什么让它发生了变化,也似乎无法让它再次出现。
public class View extends JPanel{
private Model model;

    public View(Model model) {
        this.model = model;
        //setLayout(new GridLayout(4,1));
        //add(new RadiusPanel(model));
        //add(new ColorPanel(model));
        //add(new SolidPanel(model));
        //add(new TitlePanel(model));

        repaint();
    }

    @Override
    protected void paintComponent(Graphics g){
        super.paintComponent(g);

        //center of view panel, in pixels:
        int xCenter = getWidth()/2;
        int yCenter = getHeight()/2;

        int radius = model.getRadius();
        int xStart = xCenter - radius;
        int yStart = yCenter - radius;
        int xWidth = 2 * radius;
        int yHeight = 2 * radius;
        g.setColor(model.getColor());

        g.clearRect(0, 0, getWidth(), getHeight());

        if (model.isSolid()){
            g.fillOval(xStart, yStart, xWidth, yHeight);
        } else {
            g.drawOval(xStart, yStart, xWidth, yHeight);
        }        
    }
}

这和之前的想法有点类似 - 被注释掉的代码是我添加的为了尝试让事情运作起来,但不基于提供的指示。在未被注释的情况下,我也取消了mvcFrame行中的add(new View(model));线。

各种面板类(SolidPanel、ColorPanel等)只是扩展了一个名为ControlPanel的类,该类扩展了JPanel。它们似乎都正常工作,没有遇到太多问题。还有一个驱动程序启动GUI。这个也似乎按预期工作。

我遇到的主要问题是无法显示椭圆形,并且我曾经让它显示过一次,但更改它的选项似乎都不起作用。我觉得我已经接近成功了,但此时已经不知道该尝试什么了。

任何能帮忙的人都将获得我的最诚挚感谢。

3个回答

2

这是一个非常仓促的重写版,“只是为了让它工作”。

main.java

public class main {
    public static void main(String[] args) {
        // The JFrame could be created here, since it lasts the life
        // of the program.

        //...then, later, the model.
        Model mdl = new Model();    

        // ...and then move on to applying the view and control to the frame.
    }
}

Controller.java

// Nothing interesting here, added for consistency.
public class Controller {
    private final Model model;

    public Controller(Model model) {
        // The frame is shown automatically in the model here.
        this.model = model;

        // The frame's setVisible is a control issue, should be called
        // from in here, not automatically in the model.
    }
}

MvcFrame.java

import javax.swing.JFrame;

public class MvcFrame extends JFrame {
    private final Model model;

    public MvcFrame(Model model){
        this.model = model;
        // Anytime you add anything to a JFrame, use the content pane.
        this.getContentPane().add(model.getView());

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // This line centers the frame on your screen.
        setLocationRelativeTo(null);
        setSize(800,600);
        // The frame won't paint until it's visible.
        // This means if you check dimensions, everything will be 0 x 0
        // until this line is called.
        setVisible(true);
    }
}

Model.java

import java.awt.Color;

public class Model {
    private final Controller controller;  // Not used yet
    private final View view;
    private final MvcFrame mvcFrame; // Not used yet

    // Mutators and accessors needed for these guys (set/get)
    private final int radius = 44;
    private final Color color = Color.BLUE;
    private final boolean solid = true;

    public Model() {
        controller = new Controller(this);
        view = new View(this);
        mvcFrame = new MvcFrame(this);
    }

    public View getView() {
        return view;        
    }

    public int getRadius() {
        return radius;      
    }

    public Color getColor() {
        return color;       
    }

    public boolean isSolid() {
        return solid;       
    }
}

View.java

import java.awt.Graphics;
import javax.swing.JPanel;

public class View extends JPanel{
    private final Model model;

    public View(Model model) {
        this.model = model;
    }

    @Override
    protected void paintComponent(Graphics g){
        super.paintComponent(g);

        //center of view panel, in pixels:
        int xCenter = getWidth()/2;
        int yCenter = getHeight()/2;

        int radius = model.getRadius();
        int xStart = xCenter - radius;
        int yStart = yCenter - radius;
        int xWidth = 2 * radius;
        int yHeight = 2 * radius;
        g.setColor(model.getColor());

        g.clearRect(0, 0, getWidth(), getHeight());

        if (model.isSolid()){
            g.fillOval(xStart, yStart, xWidth, yHeight);
        } else {
            g.drawOval(xStart, yStart, xWidth, yHeight);
        }        
    }
}

哇,伙计——这真是非常有帮助。我实际上已经成功让我的控制面板和椭圆形显示出来,并且在我告诉它们改变时也能够改变!不过我有一个问题——我注意到你从View构造函数中删除了repaint()函数调用。这又是一个“因为指导书上说要这么做所以我就这么做了”的情况,但我的理解是每当视图需要更新时就会调用它。这是另一个错误的指导吗? - AgentWiggles
根据设计而定。当椭圆的某些属性发生变化,但系统尚未发现需要重新绘制窗口时,应该调用repaint()方法。例如,如果在某处更改了椭圆的x位置,但没有任何可见变化 - 这是怎么回事?你调整窗口大小,它就会发生变化。这是因为窗口被重绘了。要通过编程来触发这种行为,请自行调用repaint()方法。在MVC案例中,当面板显示时,椭圆保持不变,并正常绘制,因此不需要调用repaint方法。 - Ben
在模型中使用java.awt.Color是正确的吗? - Eko

1

你的问题有点冗长,而且我不确定为什么你已经注释掉了关键部分的代码,但第一个问题是:

//add(new View(model));

你是否将视图添加到框架中?椭圆可能绘制得很好,但视图未被添加。

问题很可能出现在这段代码中:

public class MvcFrame extends JFrame {
    ...

    public MvcFrame(Model model){
        ...
        //setLayout(new GridLayout(4,1));
        //add(new RadiusPanel(model));
        ...
    }
}

当你直接调用add时,它会引用超类(JFrame)。不幸的是,JFrames很狡猾,因为它们有一个contentPane来保存布局。更加狡猾的是,这个内容窗格是一个空布局,只能通过放入你自己的面板来改变。

所以,你应该做出像这样的东西。即使你不完全遵循,这些方法也应该对你有很大帮助:

...
JPanel pnl = new JPanel(new GridLayout(4, 1));
this.setContentPane(pnl);
pnl.add(new RadiusPanel(model));
...

如果您不想明确设置内容窗格,可以使用this.getContentPane().add(foo)
null布局问题可能也会影响您的椭圆形绘图,因为当您添加JPanel时,其大小未指定,因此默认为(0,0)
此外,我不确定为什么您的控制器要扩展JPanel。您的视图应该对控制器可用,并且应该是唯一具有任何Swing组件的内容。

控制器扩展JPanel只是因为我被告知要这样做...我知道这不是最好的理由 >_> 至于你对内容窗格的指示,我正在使用getContentPane.add(foo),但我对add的理解是它会自动获取内容窗格。我要试一试 - 在mvcFrame中具有这些add函数,而不是控制器,这是否在上下文中看起来正确?这是我的怀疑,但它与说明相反,所以我仍然有点困惑(尽管开始认为我只是有糟糕的说明)。 - AgentWiggles
请在2分钟后回来,我已经重写了整个程序,你可以查看一下。 - Ben
非常感谢 - 我现在要去试试它 - 奇怪的代码注释背后的原因是它们与项目的指导方针不一致,但由于它们似乎做了一些正确的事情,我想把这些代码包括进来。 - AgentWiggles
请再等2分钟,我会在重写中添加一些有用的注释。 - Ben
我已经在重写中添加了一些注释,这将帮助您并提供一些要尝试的内容。我得走了,祝你好运。 - Ben
显示剩余2条评论

0

虽然我的问题不同,但是这一行代码:

g.clearRect(0, 0, getWidth(), getHeight());

对于我的项目解决了所有问题,因为它没有擦除先前的椭圆形,所以我只能在将其变大时看到更改。谢谢。


这可能最好作为评论添加,而不是答案,因为它不能解决原帖作者的问题。 - Dave Challis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接