我的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中的按钮消失了
的图片提前谢谢
答案 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。你肯定会想避免使用它们。
建议
protected void paintComonent(Graphics g)
方法,并使用模型中的Car信息绘制汽车。换句话说,使用模型的状态来影响视图的状态。 修改强>
你的主要错误是:
class Car extends JPanel {
private int id, x, y, vitess;
//....
public int getX() {
return x;
}
public int getY() {
return y;
}
你无意中重写了汽车JPanel的getX和getY方法,弄乱了这些组件的位置。除非必要,否则这是避免覆盖Swing组件的另一个原因 - 避免这些隐藏的副作用。
删除或重命名这些方法。