简体   繁体   English

Java Jpanel重涂/更新问题

[英]Java Jpanel Repaint/update probleme

i have a problem with the method Repaint of my jpanel. 我对jpanel的Repaint方法有问题。 I'm trying to make a "Racing game" with the Java Swing and by following the MVC architecture : 我正在尝试使用Java Swing并遵循MVC架构来制作“赛车比赛”:

i have 5 classes : Main : runs the 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);
    }
}

Model : 型号:

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

the View : 风景 :

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

The controller: 控制器:

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

The car class : 车类:

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 class: 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 you run the project you will note that the cars does not reach the end of the frame, even if used: 如果您运行该项目,则将注意,即使使用这些汽车,它们也不会到达框架的末端:

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

but when i replace 但是当我更换

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

with

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

the cars can now reach the end of the Frame, but the buttons in the btnJpanel disappear 现在,汽车可以到达车架的尽头了,但是btnJpanel中的按钮消失了

image with repaint here image with update here 图像在这里重画图像在这里更新

thank you in advance 先感谢您

I can't say that I've read all your code, but your model never notifies the view. 我不能说我已经阅读了所有代码,但是您的模型从不通知视图。 I mean nowhere inside of the model class's code is the notifyView() method called. 我的意思是,在模型类的代码中,没有地方调用notifyView()方法。 It should be the model's responsibility to call this whenever its state is changed. 每当其状态发生更改时,模型都有责任调用它。

Note that this: 注意:

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

Should not be used as the Graphics obtained is not stable. 不应使用,因为获得的图形不稳定。

Also, your model holds view components, an ArrayList of Car, a class that extends JPanel, another thing that should never occur as the model should be completely ignorant of the view, with the exception being that it knows that some things may be listening to it, and it needs to notify those things, and that's it. 此外,您的模型包含视图组件,Car的ArrayList,扩展JPanel的类,不应发生的另一件事,因为模型应完全不了解视图,但它知道某些内容可能正在侦听它,它需要通知那些事情,仅此而已。 Instead your Model should hold an ArrayList of non-view, non-JPanel logical Car objects. 相反,您的模型应该包含一个非视图,非JPanel逻辑Car对象的ArrayList。


Edit 编辑
You state: 您声明:

In the model i have the method: public void notifyView() which is called by the car's method public void Move() , That means whenever the thread calls the move method of the car, it calls the notifyView of the model 在模型中,我有方法:public void notifyView()由汽车的方法调用public void Move(),这意味着每当线程调用汽车的move方法时,它都会调用模型的notifyView

No, the Car should not be calling this method -- only the model itself should call this when its state is changed. 不,Car不应调用此方法,只有模型本身在状态更改时才应调用此方法。

Also, I see that you've got very dangerous code using Thread#suspend() and Thread#resume() method calls. 另外,我看到使用Thread#suspend()Thread#resume()方法调用的代码非常危险。 These methods have been deprecated as they've been found to be dangerous. 这些方法被认为是危险的,因此已被弃用。 To find out why, please check the API for Thread as well as this useful article . 要找出原因,请查看Thread API这篇有用的文章 You will most definitely want to avoid their use. 您绝对肯定会避免使用它们。


Suggestions 建议

  • Make Car a logical non-GUI class that knows its position and can have its position changed. 使Car成为逻辑非GUI类,该类知道其位置并且可以更改其位置。
  • Override your drawing JPanels protected void paintComonent(Graphics g) method and use the Car information from the model to draw the Cars. 覆盖图形JPanels protected void paintComonent(Graphics g)方法,并使用模型中的Car信息绘制Cars。 In other words, use the state of the model to affect the state of the view. 换句话说,使用模型的状态来影响视图的状态。
  • Use a Swing Timer instead of a background thread, to change your Car positions. 使用Swing Timer而不是后台线程来更改您的汽车位置。
  • Have the model and only the model call the notifyView method when its state has been changed. 让模型并且只有模型在状态改变时才调用notifyView方法。

Edit 编辑
Your main bug is here: 您的主要错误在这里:

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

    //....

    public int getX() {
       return x;
    }

    public int getY() {
       return y;
    }

You're inadvertently overriding the Car JPanel's getX and getY methods, messing up the location of these components. 您无意间覆盖了Car JPanel的getX和getY方法,从而弄乱了这些组件的位置。 This is another reason to avoid overriding Swing components unless necessary -- to avoid these hidden side effects. 这是避免除非必要而覆盖Swing组件的另一个原因-避免这些隐藏的副作用。

Get rid of or rename these methods. 摆脱或重命名这些方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM