Java Jpanel重绘/更新问题

时间:2014-11-23 20:17:06

标签: java swing jpanel repaint

我的jpanel重绘方法有问题。 我试图制作一个赛车游戏"使用Java Swing并遵循MVC架构:

我有5个班级: Main:运行MVC

public class Main {
    private static Model model;
    private static View view;
    private static Controller controller;

    public static void main(String[] args) {
        model = new Model();
        view =  new View();
        controller = new Controller();

        model.addObserver(view);

        controller.addModule(model);
        controller.addView(view);

        view.addContoller(controller);
    }
}

型号:

import java.util.ArrayList;
import java.util.Observable;


public class Model extends Observable{
    private ArrayList<Car> cars;// List of cars

    public Model() {
        cars = new ArrayList<Car>();
        cars.add(new Car(this,0, 50));
        cars.add(new Car(this,0, 50));
        cars.add(new Car(this,0, 50));
    }

    public void startCar(int i){
        //i is the index of the selected element in the checkbox
        //if i==0 then the selected element is "All cars" else is a specific car
        if(i>0)
            cars.get(i-1).start();
        else{
            for(int j=0;j<cars.size();j++)
                cars.get(j).start();
        }
    }

    public void speedUpCar(int i) {
        if(i>0)
            cars.get(i-1).incVitess();
        else{
            for(int j=0;j<cars.size();j++)
                cars.get(j).incVitess();
        }
    }

    public void notifyView(){
        setChanged();
        notifyObservers(cars);
    }

    public void speedDownCar(int i) {
        if(i>0)
            cars.get(i-1).decVitess();
        else{
            for(int j=0;j<cars.size();j++)
                cars.get(j).decVitess();
        }
    }

    public void stopCar(int i) {
        if(i>0)
            cars.get(i-1).stop();
        else{
            for(int j=0;j<cars.size();j++)
                cars.get(j).stop();
        }
    }
}

视图:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class View implements Observer {
    private JFrame fen;
    private JPanel btnPanel,panel;
    private JButton play,speedUp,speedDown,stop;
    private JComboBox<String> listeCar;
    private boolean test = false;

    public View() {
        fen = new JFrame();
        fen.setTitle("Car Racing");
        fen.setSize(900, 400);
        fen.setLocationRelativeTo(null);
        fen.setResizable(false);

        Vector<String> v = new Vector<String>();
        v.add("All cars");v.add("car 1");v.add("car 2");v.add("car 3");
        listeCar = new JComboBox<String>(v);

        play = new JButton("Play");
        speedUp = new JButton("+");
        speedDown = new JButton("-");
        stop = new JButton("stop");

        panel = new JPanel(new GridLayout(3,1));
        btnPanel = new JPanel();

        btnPanel.add(listeCar);
        btnPanel.add(play);
        btnPanel.add(speedUp);
        btnPanel.add(speedDown);
        btnPanel.add(stop);

        Container c = fen.getContentPane();

        c.setLayout(new BorderLayout());

        c.add(btnPanel, BorderLayout.SOUTH);
        c.add(panel, BorderLayout.CENTER);

        fen.setVisible(true);

        fen.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });


    }

    public void addContoller(Controller controller){
        play.addActionListener(controller);
        speedUp.addActionListener(controller);
        speedDown.addActionListener(controller);
        stop.addActionListener(controller);
    }

    @Override
    public void update(Observable arg0, Object c) {
        ArrayList<Car> cars = (ArrayList<Car>)c;
        for(int i=0;i<cars.size();i++){
            Car car = cars.get(i);
            if(!test){ // if its the first tima, add the cars to the panel
                panel.add(car);
            }else{
                car.repaint(); // << the Problem is HERE
            }
        }
        test = true;
    }

    public JButton getPlay() {
        return play;
    }

    public JButton getSpeedUp() {
        return speedUp;
    }

    public JButton getSpeedDown() {
        return speedDown;
    }

    public JButton getStop() {
        return stop;
    }

    public JComboBox<String> getListeCar() {
        return listeCar;
    }
}

控制器:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


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

    public Controller() {

    }

    public void addModule(Model m) {
        model = m;
        model.notifyView();
    }

    public void addView(View v){
        view = v;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == view.getPlay()){
            model.startCar(view.getListeCar().getSelectedIndex());
        }else if(e.getSource() == view.getSpeedUp()){
            model.speedUpCar(view.getListeCar().getSelectedIndex());
        }else if(e.getSource() == view.getSpeedDown()){
            model.speedDownCar(view.getListeCar().getSelectedIndex());
        }else if(e.getSource() == view.getStop()){
            model.stopCar(view.getListeCar().getSelectedIndex());
        }
    }
}

汽车类:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JPanel;


public class Car extends JPanel{
    private int id,x,y,vitess;
    private Thread thread;
    private Model model;
    private boolean start = true;
    private boolean forward = true;
    private Color color;
    private boolean threadStarted = false;
    private BufferedImage bg; // background image

    public Car(Model model,int x,int y) {
        this.x =x;
        this.y = y;
        vitess = 7;
        this.model = model;

        try {
            bg = ImageIO.read(new File("road.png"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        color = changeColor(); // Random color

        thread = new Thread(new CarThread(this));

        start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(bg, 0, 0, null); 
        g.setColor(color);
        g.fillRect(x, y, 100, 50); // the car is a simple rectangle
    }

    public void start() {
        start = true;
        if(!threadStarted){
            threadStarted = true;
            thread.start();
        }else{
            thread.resume();
        }
    }

    public void move(){
        System.out.println("X:"+x);
        if(forward){
            if(x<this.getWidth()){
                x+=2;
            }else{
                color = changeColor();
                forward = false;
            }
        }else{
            if(x>0){
                x-=2;
            }else{
                color = changeColor();
                forward = true;
            }
        }

        model.notifyView();
    }

    private Color changeColor() {
        int r = (int)(Math.random()*255);
        int g = (int)(Math.random()*255);
        int b = (int)(Math.random()*255);
        return new Color(r,g,b);
    }

    public void stop(){
        start = false;
        thread.suspend();
    }

    public void incVitess(){
        if(vitess>1)
            vitess--;
    }

    public void decVitess(){
        if(vitess<6)
            vitess++;
    }

    public int getId() {
        return id;
    }


    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getVitess() {
        return vitess;
    }   

    public boolean getStart(){
        return start;
    }

    public void setStart(boolean m){
        this.start = m;
    }

    public Color getColor(){
        return color;
    }
}

CarThraed课程:

public class CarThread implements Runnable{
    private Car car;

    public CarThread(Car car) {
        this.car = car;
    }

    @Override
    public void run() {
        while(car.getStart()){
            car.move();
            try {
                Thread.sleep(car.getVitess());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

如果您运行该项目,您将注意到即使使用了汽车也没有到达框架的末端:

if(x<this.getWidth()) x++;

但是当我替换

car.repaint(); // << the Problem is HERE

car.update(car.getGraphics()); // << the Problem is HERE

汽车现在可以到达框架的末端,但是btnJpanel中的按钮消失了

带重绘here的图片 更新here

的图片

提前谢谢

1 个答案:

答案 0 :(得分:2)

我不能说我已经阅读了所有代码,但您的模型从未通知视图。我的意思是模型类的代码里面没有调用notifyView()方法。每当状态发生变化时,应该由模型负责调用它。

请注意:

car.update(car.getGraphics()); // << the Problem is HERE

不应该使用获得的图形不稳定。

此外,你的模型包含视图组件,一个Car的ArrayList,一个扩展JPanel的类,另一个不应该发生的事情,因为模型应该完全不知道视图,除了它知道某些东西可能正在倾听它,它需要通知那些事情,就是这样。相反,您的Model应该包含非视图,非JPanel逻辑Car对象的ArrayList。


修改
你说:

  

在模型中我有方法:public void notifyView()由汽车的方法调用public void Move(),这意味着每当线程调用汽车的move方法时,它调用模型的notifyView < / p>

不,Car不应该调用这个方法 - 只有模型本身应该在状态改变时调用它。

另外,我发现您使用Thread#suspend()Thread#resume()方法调用的代码非常危险。这些方法已被弃用,因为它们被发现是危险的。要了解原因,请查看API for Thread以及this useful article。你肯定会想避免使用它们。


建议

  • 使Car成为一个逻辑非GUI类,知道其位置并可以更改其位置。
  • 覆盖您的绘图JPanels protected void paintComonent(Graphics g)方法,并使用模型中的Car信息绘制汽车。换句话说,使用模型的状态来影响视图的状态。
  • 使用Swing Timer代替后台线程来更改您的Car位置。
  • 模型和模型在状态发生变化时调用notifyView方法。

修改
你的主要错误是:

class Car extends JPanel {
    private int id, x, y, vitess;

    //....

    public int getX() {
       return x;
    }

    public int getY() {
       return y;
    }

你无意中重写了汽车JPanel的getX和getY方法,弄乱了这些组件的位置。除非必要,否则这是避免覆盖Swing组件的另一个原因 - 避免这些隐藏的副作用。

删除或重命名这些方法。