【问题标题】:Alternative to painting many objects onto a JPanel将许多对象绘制到 JPanel 上的替代方法
【发布时间】:2013-03-31 23:20:39
【问题描述】:

我将重写下面的类以使用构造函数并做一些更智能的事情(而不是像四个 producer() 这样的方法 - 所以不用担心。

目前我遍历这四个数组列表,然后将对象绘制到 JPanel 上。在大约 2-3000 个对象之后,这可能会导致一些显着的延迟。

我可以用什么来替代这些数组列表?某种类型的 JSON/XML 序列化会起作用吗?在我的专业编码(而不是这个简单的副项目)中,我使用了一个庞大的系统,该系统使用 DataNucleus 将模型类转换为 SQL 中的表结构——我觉得这对于我有趣的副项目来说将是巨大的矫枉过正。想法?

public class Life {
PlayPanel panel;
public int prodSeed;
public int herbSeed;
public int predSeed;
public int decoSeed;
public ArrayList<Producer> prodStore = new ArrayList<Producer>();
public ArrayList<Herbivore> herbStore = new ArrayList<Herbivore>();
public ArrayList<Predator> predStore = new ArrayList<Predator>();
public ArrayList<Decomposer> decoStore = new ArrayList<Decomposer>();


public Life() {
}

public void setPanel(PlayPanel p) {
    panel = p;
}
public void producers() {
    for(int j = 0; j < prodSeed; j++) {
        Producer p = new Producer(panel.life);
        prodStore.add(p);
    }
    panel.producerPaint = true;
}
public void herbivores() {
    for(int j = 0; j < herbSeed; j++) {
        Herbivore h = new Herbivore(panel.life);
        herbStore.add(h);
    }
    panel.herbivorePaint = true;
}
public void predators() {
    for(int j = 0; j < predSeed; j++) {
        Predator p = new Predator(panel.life);
        predStore.add(p);
    }
    panel.predatorPaint = true;
}
public void decomposers() {
    for(int j = 0; j < decoSeed; j++) {
        Decomposer d = new Decomposer(panel.life);
        decoStore.add(d);
    }
    panel.decomposerPaint = true;
}
public void beginAction() {
    for (int j = 0; j < prodStore.size(); j++) {
        prodStore.get(j).behavior();panel.repaint();        
    }
    for (int j = 0; j < herbStore.size(); j++) {
        herbStore.get(j).behavior();panel.repaint();
    }
    for (int j = 0; j < predStore.size(); j++) {
        predStore.get(j).behavior();panel.repaint();
    }
    for (int j = 0; j < decoStore.size(); j++) {
        decoStore.get(j).behavior();panel.repaint();
    }
    //Use booleans between these for loops to not repaint an extra amount of action

}

JPanel 类中非常痛苦的paintComponent 方法:

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if(producerPaint == true) {
        //int j = 0;

        while(j < life.prodStore.size()) {
            g.setColor(new Color(19,145,14));
            g.fillOval(life.prodStore.get(j).getxAxis(), life.prodStore.get(j).getyAxis(), life.prodStore.get(j).getxSize(), life.prodStore.get(j).getySize());
            j++;
        }
        j=0;
    }
    if(herbivorePaint == true) {
        //int j = 0;

        while(j < life.herbStore.size()) {
            g.setColor(new Color(228,226,158));
            g.fillOval(life.herbStore.get(j).getxAxis(), life.herbStore.get(j).getyAxis(), life.herbStore.get(j).getxSize(), life.herbStore.get(j).getySize());
            j++;
        }
        j=0;
    }
    if(predatorPaint == true) {
        //int j = 0;

        while(j < life.predStore.size()) {
            g.setColor(new Color(207,87,87));
            g.fillOval(life.predStore.get(j).getxAxis(), life.predStore.get(j).getyAxis(), life.predStore.get(j).getxSize(), life.predStore.get(j).getySize());
            j++;
        }
        j=0;
    }
    if(decomposerPaint == true) {
        //int j = 0;

        while(j < life.decoStore.size()) {
            g.setColor(Color.white);
            g.fillOval(life.decoStore.get(j).getxAxis(), life.decoStore.get(j).getyAxis(), life.decoStore.get(j).getxSize(), life.decoStore.get(j).getySize());
            j++;
        }
        j=0;
    }
}

作为一个相关的附注:在我处理这段代码时,我将不再使用布尔值(项目已经一两年了,在我知道自己在做什么之前)!

【问题讨论】:

    标签: java sql serialization arraylist


    【解决方案1】:

    在一个组件上绘制很多很多项目不是问题,但它不应该发生在paintComponent 内部。您要做的是拥有一个单独的图像(例如BufferedImage),在该图像上进行绘制,然后您的paintComponent 仅负责将该图像绘制到 JPanel 上。因此,您的用户并没有看到所有这些被一一绘制的东西。他们只看到最终产品。

    在“Java 中的双缓冲”中搜索有关此想法的精彩演示。

    对于数据结构,保留 4 个列表就可以了,但您可能希望拥有更多在所有对象之间共享的抽象绘图实用程序。例如,您绘制它们的方式几乎相同,除了颜色,因此创建一个接受超类(例如BiologyObject)到任何这些子类(例如ProducerDecomposer)的绘图方法等)作为参数。如果这没有意义,请告诉我。

    更新:在 JPanel 上绘制的正确方法

    private BufferedImage image;
    
    public MyClass() {
        image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_ARGB);
    }
    
    public void paintOntoImage() {
        //here is where you would draw all the things that you currently have in your paintComponent method
        Graphics2D g = (Graphics2D) image.getGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, WIDTH, HEIGHT);
    
        //now you can call "g.fillOval" or any other methods on "g"
    }
    
    public void paintComponent(Graphics g) {
        g.drawImage(image, 0, 0, null);
    }
    

    【讨论】:

    • 好吧,回到双缓冲似乎让我想到了这段出色的代码:动力学模型 (sites.google.com/site/drjohnbmatthews/kineticmodel)。我在自己的代码中实现它时遇到了麻烦,我想我会回去看看我能做什么!
    • 这对我来说似乎有点过头了。让我在答案中添加一些代码。请稍后再回来查看
    • 好的:如果我使用像您向我展示的那样的关键挑战:我在 paintOntoImage() 的末尾放置了一个 repaint() 以便让图像在 JPanel “实时”上更新?其次,清除旧图像如何?只是在图像对象上绘制任何新内容之前将其清空?
    • 是的,repaint() 可以解决问题。要清除图像,只需 g.setColor(Color.white) 和 g.fillRect(0,0,WIDTH,HEIGHT);
    • 酷。我现在运行正常。不幸的是,这实际上比在我原来的帖子中使用以前的方法要慢得多(它似乎每秒轻弹/切换到下一张图像,而不是之前的连续重绘[在大约 2000 个元素后变得迟缓])。我敢打赌,这是一个循环遍历这四个数组列表的人工制品,以及我可以找到一种方法来实现的各种其他优化。我会继续调查!
    猜你喜欢
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 2014-10-02
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    • 2017-06-27
    相关资源
    最近更新 更多