使用线程在Java中绘制面板 [英] Using threads to paint panel in java

查看:121
本文介绍了使用线程在Java中绘制面板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个具有许多不同视图的程序.其中之一是图形密集型(显示互连的图形).其他人只显示小的但复杂的图.

I am writing a program that has a number of different views. One of which is fairly graphics intensive (it displays an interconnected graph). Others just display small but complex diagrams.

我发现主视图的绘制时间很长(即使只是绘制当前可见的区域),并且在绘制时,界面的其余部分变得很慢.

I'm finding the paint time for the main view is quite long (even just painting the currently visible area) and while it is being painted, the rest of the interface becomes very slow.

我的问题是,我可以创建一个新线程来处理绘画吗?如果是这样,是否会导致性能提高,我怀疑它不会.我尝试了以下方法:

My question is this, can I create a new thread to handle the painting - and if so, will it result in a performance increase, my suspicion is that it wont. I have tried the following:

创建一个抽象类ThreadPaintablePanel,我的复杂视图将继承该类.

creating an abstract classs ThreadPaintablePanel, that my complex views inherit from.

public abstract class ThreadPaintablePanel extends JPanel{
 private Thread painter;
 public abstract void paintThread(Graphics g);

 protected void callPaintThread(Graphics g){
   if(painter != null){
     painter.interrupt();
   }
   painter = new Thread(new PaintRunnable(this, g));
   painter.start();
 }
} 

然后在我的复杂视图中,我的paintComponent方法很简单:super.callPaintThread(g);

Then in my complicated views my paintComponent method is simply: super.callPaintThread(g);

被覆盖的paintThread方法包含我所有的绘画代码.但是,这将导致未上漆的面板.我错过了明显的事情吗?

The overridden paintThread method contains all my painting code. However this results in unpainted panels. Have I missed something obvious?

谢谢

推荐答案

除了多缓冲技术.它涉及两个步骤:

You cannot let any thread but the Event Dispatch Thread (EDT) touch the GUI. Letting other threads mess with the GUI causes trouble and Exceptions. You can employ a multi-threaded multi-buffering technique. It involves two steps:

  1. 为了并行化复杂的绘制例程,您可以简单地将整个视图"划分为补丁,然后让一个线程将一个补丁绘制为一张图像. 此处是有关在Java中处理图像的教程.

一旦有了图像,就可以覆盖paintComponent并使用

Once you have the images, you can override paintComponent and use the Graphics.drawImage method to let the EDT display the full or a partial view, by stitching the images of the corresponding patches together.

为了避免不必要的工作,请确保先执行第一步,然后再更改视图后再执行,否则只需再次绘制先前计算的结果即可.此外,如果您可以缩小视图内部在帧之间已更改的区域的范围,请尝试仅更新部分补丁.

In order to avoid unnecessary work, make sure to perform the first step initially and then only after the view changed, else just draw previously computed results again. Furthermore, try to only update part of the patches, if you can narrow down the regions inside the views that have changed between frames.

让我们假设您的视图至少与最佳线程数一样多,因此我们可以垂直划分视图.另外,我们假设绘制任何像素所需的工作量与其他像素相同,因此我们可以为每个色块使用相同的大小.这两个假设使事情变得容易得多.

Let us assume that your view is at least as many pixels high as your optimal number of threads, so we can just partition the view vertically. Also, let us assume that drawing any pixel takes about the same amount of work as any other, so we can use the same size for every patch. These two assumptions make things a lot easier.

代码如下.如果您不需要计算机执行其他任何操作,则可以nThreads设置为您的核心数量.请注意,该代码还将伪代码用于"parallel for",在此处中对此进行了解释:

Code follows. If you don't need your computer to do anything else, you can set nThreads to your number of cores. Note that the code also uses pseudocode for "parallel for" which is explained here:

// let multiple threads write all patches
public BufferedImage[] writePatches(...)
{
    // Given data:
    const int nThreads = ...;         // the amount of threads that you want
    Rectangle viewBox = ...;        // The view rectangle

    // Immediate data:
    Dimension viewSize = viewBox.getSize();
    int defaultPatchHeight = (int)ceil((float)viewSize.height / nThreads);
    int lastPatchHeight = viewSize.height - (nThreads-1) * defaultPatchHeight;

    // The actual "buffer" is a set of images
    BufferedImage[] images = new BufferedImage[nThreads];

    // ...

    // pseudocode for parallel processing of a for loop
    parallel-for (nThreads, threadId)
    {
        // the starting point and size of this thread's patch
        // handle boundary (last) patch
        int w = viewBox.width;
        int h = threadId == nThread-1 ? lastPatchHeight : defaultPatchHeight;                
        int x = viewBox.x;
        int y = viewBox.y + threadId * defaultPatchHeight;

        BufferedImage patch = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = off_Image.createGraphics();

        // use g to draw to patch image here
        // better yet: Re-use existing patches and only update the parts that changed.

        images[threadId] = patch;
    }
    return images;
}

// ...

// override paintComponent
@Override
void paintComponent(Graphics gg)
{
    Graphics2D g = (Graphics2D) gg;
    // ...

    // stitch all images together (you can also just display only some images here)
    for (int threadId = 0; threadId < nThreads; ++threadId)
    {
        int w = viewBox.width;
        int h = threadId == nThread-1 ? lastPatchHeight : defaultPatchHeight;                
        int x = viewBox.x;
        int y = viewBox.y + threadId * defaultPatchHeight;

        // use pre-computed images here
        BufferedImage patch = images[threadId];
        g.drawImage(patch, x, y, ...);
    }
}

这篇关于使用线程在Java中绘制面板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆