前面是是废话,不爱看的直接从"//Here"开始看
简单说一下,这是我刚学完JavaSE自己在做一个小的练手项目时遇到的问题。
我把遇到的问题写了下面这个简单的测试程序来凸显这个问题。
主要是这样的,我最开始写移动的功能,是在paint方法里改坐标然后起个线程repaint,后来发现这样实在不好,当手动重绘时(比如最小化和最大化,拖动窗口在屏幕边上乱晃),由于重绘时调用paint的次数会更高,程序中运动的图形就会明显变快了。后来在CSDN提问,得到了解答,不要在paint上增加坐标,直接起个线程调用repaint和move方法。后来程序越来越复杂,又要加容器,又要加这个那个,删了又删,改了又改,到了现在这种情况。
//Here
这个测试程序主要的功能就是killer敲一下A键打出一个子弹,消灭前面的一个somebody,为了表达更清楚,其他的功能都没写。
Gun(或者说子弹)是用ArrayList装的,只有在List是!isEmpty时才会调用线程中if语句里的移动和重绘,这样会大大减少CPU资源的浪费。但当打到对面的somebody时本应该是要子弹和对方一起消失的,把当前的子弹从List中remove掉这时肯定的,但可惜的是remove掉以后List将是空的,不能repaint,子弹和被打中的家伙就停在那里哥俩好了。大家可以试试,按一下A键打中后就停在那里了,想刷掉就再打一颗就能调用repaint了。
还有一个问题就是为什么程序总是莫名其妙的报ConcurrentModificationException呢,有时候是打前3颗子弹就报,有时候打了几百发也不报,完全没有规律,不晓得这到底是什么。
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;public class Main {
public static void main(String[] args) {
new MainFrame();
}
}class MainFrame extends Frame {

Image offScreenImage = null;
List<Gun> guns = new ArrayList<Gun>();
People killer = new People(50, 150, true, this);
People sb = new People(300, 150, false, this);

private class GunThread implements Runnable {
public void run() {
while(true) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!guns.isEmpty()) {
repaint();
for(Gun gun : guns) {
gun.move();
}
}
}
}
}

public MainFrame() {
this.setSize(400, 400);
this.setBackground(Color.BLUE);
this.setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
killer.KeyReleased(e);
}
});
new Thread(new GunThread()).start();
} public void paint(Graphics g) {
killer.draw(g);
sb.draw(g);
for(int i=0; i<guns.size(); i++) {
Gun gun = guns.get(i);
gun.killsb(sb);
if(null != gun) {
gun.draw(g);

}
}

public void update(Graphics g) {
if(null == offScreenImage) {
offScreenImage = this.createImage(400, 400);
}
Graphics gOffScreen = offScreenImage.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.BLUE);
gOffScreen.fillRect(0, 0, 400, 400);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
}class People {
MainFrame mf = null;
private boolean live = true;
private boolean kill = true;
private int x;
private int y;

public People(int x, int y, boolean kill, MainFrame mf) {
this.x = x;
this.y = y;
this.setKill(kill);
this.mf = mf;
}

void draw(Graphics g) {
if (!isLive()) {
return;
}
Color c = g.getColor();
if (isKill()) {
g.setColor(Color.RED);
} else {
g.setColor(Color.YELLOW);
}
g.fillOval(x, y, 40, 40);
g.setColor(c);
}

public Rectangle getRect() {
return new Rectangle(x, y, 40, 40);
}

public void KeyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(KeyEvent.VK_A == key) {
mf.guns.add(fire());
}
}

public Gun fire() {
Gun gun = new Gun(x, y, this.mf);
return gun;
}

public boolean isLive() {
return live;
}

public void setLive(boolean live) {
this.live = live;
}

public boolean isKill() {
return kill;
}

public void setKill(boolean kill) {
this.kill = kill;
}
}class Gun {
private boolean live = true; private int x;
private int y;
private MainFrame mf = null; public Gun(int x, int y, MainFrame mf) {
this.x = x;
this.y = y;
this.mf = mf;
this.setLive(true);
} void draw(Graphics g) {
if (!isLive()) {
mf.guns.remove(this);
}
g.fillOval(x, y, 10, 10);
}

public boolean killsb(People p) {
if(getRect().intersects(p.getRect())) {
p.setLive(false);
this.setLive(false);
return true;

}
return false;
}

public Rectangle getRect() {
return new Rectangle(x, y, 10, 10);
}

void move() {
x += 3;
}

public boolean isLive() {
return live;
} public void setLive(boolean live) {
this.live = live;
}
}

解决方案 »

  1.   


     while(true) {
                    try {
                        Thread.sleep(40);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if(!guns.isEmpty()) {
                        for(Gun gun : guns) {
                            gun.move();
                        }
                    }
                    repaint();
                }    
     把repaint();放出来,第二个问题我这里运行没有出来,所以不知道怎么回答楼主。
      

  2.   

    单纯的要完成功能的方法有很多,个人以为要写个需要不停repaint的程序还不如不写,睡眠时间短点线程多点项目大点就会极耗系统资源。这并不是马士兵的坦克大战,学完JavaSE和JDBC后我正在写个多功能的联网登录版的超级玛丽来练习各种知识点和项目经验,其实还是得到了不少经验的。项目都写了上万行了都发上来不太现实所以随手敲了测试代码,是怕自己语言表达不好给回答者当做辅助的。马士兵的教程我也看过几眼,可能也是受他那个小圈圈影响,敲测试代码也g了几个filloval,不过说实话我比他写得好多了,他那个聊天和坦克大战的项目都没法用的,全是BUG。
      

  3.   

    马士兵也就忽悠忽悠J2SE基础 他写那个叫项目吗 号称自己读过3遍TCP/IP详解 结果写出的chat程序随便测试一下就一大堆异常 你们在看看楼主仅有的两篇帖子 一个是要在手动重绘不影响图形移动 一个是重绘要在必要时进行 马士兵的tankwar做到哪点了
    楼主的问题我想了想 假如使用isEmpty()是一种方法的话 那么只需要控制一下睡眠就可以了 打到后先执行repaint() 睡一会再remove() 相应的改小一下重绘线程的睡眠时间和move()的距离 不用测试 把update()注掉就能知道重画没重画了 但这也会导致很多问题 比如子弹速度不均匀 ConcurrentModificationException发生几率成倍增长 这些都是绝对可以修改的 仔细分析一下流程 debug一下 多分析分析 改改位置就可以了 至于ConcurrentModificationException 我看楼主还是有一定水平的 这个异常的发生原因很固定 解决方法也很固定 你自己google一下便可知
      

  4.   

    改了下
    1、用一个标志位控制在guns为空之后,最后repaint()一次
    2、当guns里还有本元素只时,才调用remove,防止异常import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import java.util.List;public class Main {
        public static void main(String[] args) {
            new MainFrame();
        }
    }class MainFrame extends Frame {
        
        Image offScreenImage = null;
        List<Gun> guns = new ArrayList<Gun>();
        People killer = new People(50, 150, true, this);
        People sb = new People(300, 150, false, this);
        boolean emtpy = false;
        
        private class GunThread implements Runnable {
            public void run() {
                while(true) {
                    try {
                        Thread.sleep(40);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(!guns.isEmpty()) {
                     repaint();
                        for(Gun gun : guns) {
                            gun.move();
                        }
                    } else {
                     if (!emtpy) {//用这个标志控制guns为空之后最后repaint()一次;
                     repaint();
                     emtpy = true;
                     }
                    }
                }    
            }    
        }
        
        public MainFrame() {
            this.setSize(400, 400);
            this.setBackground(Color.BLUE);
            this.setVisible(true);
            addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    setVisible(false);
                    System.exit(0);
                }
            });
            addKeyListener(new KeyAdapter() {
                public void keyReleased(KeyEvent e) {
                    killer.KeyReleased(e);
                }
            });
            new Thread(new GunThread()).start();
        }    public void paint(Graphics g) {
            killer.draw(g);
            sb.draw(g);
            for(int i=0; i<guns.size(); i++) {
                Gun gun = guns.get(i);
                gun.killsb(sb);
                if(null != gun) {
                    gun.draw(g);
                } 
            }
        }
        
        public void update(Graphics g) {
            if(null == offScreenImage) {
                offScreenImage = this.createImage(400, 400);
            }
            Graphics gOffScreen = offScreenImage.getGraphics();
            Color c = gOffScreen.getColor();
            gOffScreen.setColor(Color.BLUE);
            gOffScreen.fillRect(0, 0, 400, 400);
            gOffScreen.setColor(c);
            paint(gOffScreen);
            g.drawImage(offScreenImage, 0, 0, null);
        }
    }class People {
        MainFrame mf = null;
        private boolean live = true;
        private boolean kill = true;
        private int x;
        private int y;
        
        public People(int x, int y, boolean kill, MainFrame mf) {
            this.x = x;
            this.y = y;
            this.setKill(kill);
            this.mf = mf;
        }
        
        void draw(Graphics g) {
            if (!isLive()) {
                return;
            }
            Color c = g.getColor();
            if (isKill()) {
                g.setColor(Color.RED);
            } else {
                g.setColor(Color.YELLOW);
            }
            g.fillOval(x, y, 40, 40);
            g.setColor(c);
        }
        
        public Rectangle getRect() {
            return new Rectangle(x, y, 40, 40);
        }
        
        public void KeyReleased(KeyEvent e) {
            int key = e.getKeyCode();
            if(KeyEvent.VK_A == key) {
                mf.guns.add(fire());
                mf.emtpy = false;//标志位变化
            }
        }
        
        public Gun fire() {
            Gun gun = new Gun(x, y, this.mf);
            return gun;
        }
        
        public boolean isLive() {
            return live;
        }
        
        public void setLive(boolean live) {
            this.live = live;
        }
        
        public boolean isKill() {
            return kill;
        }
        
        public void setKill(boolean kill) {
            this.kill = kill;
        }
    }class Gun {
        private boolean live = true;    private int x;
        private int y;
        private MainFrame mf = null;    public Gun(int x, int y, MainFrame mf) {
            this.x = x;
            this.y = y;
            this.mf = mf;
            this.setLive(true);
        }    void draw(Graphics g) {
            if (!isLive()) {
             if (mf.guns.contains(this)) {//防异常处理
                    mf.guns.remove(this);
             }
            }
            g.fillOval(x, y, 10, 10);
        }
        
        public boolean killsb(People p) {
            if(getRect().intersects(p.getRect())) {
                p.setLive(false);
                this.setLive(false);
                return true;
                
            }
            return false;
        }
        
        public Rectangle getRect() {
            return new Rectangle(x, y, 10, 10);
        }
        
        void move() {
            x += 3;
        }
        
        public boolean isLive() {
            return live;
        }    public void setLive(boolean live) {
            this.live = live;
        }
    }
      

  5.   

    当然,这种修改和往破衣服上打补丁没什么区别
    如果楼主还希望越写越大的话
    最好先把结构定好了再开始写然后,随着你的程序“越来越大”
    你这样的repaint()控制可能还是无法满足少占资源的要求
    你可能需要用算法来控制,仅仅repaint()   Frame中必须要repaint()的区域
    而不是每次repaint()都是repaint()整个Frame
      

  6.   

    哥们你这方法我有想过啊,只是觉得不太规范而已,昨天也粗略的查了一下ConcurrentModificationException,就是在遍历和remove时会导致他内部的两个值不一致,应该等遍历完后一起removeAll。不过昨天太困了没仔细看,我再去看看。