简体   繁体   English

Java Swing中的分层画布

[英]Layered Canvas in Java Swing

I am simulating persons walking on a graph of connected nodes. 我正在模拟在连接的节点图上行走的 To display it visually I use the canvas object. 为了直观地显示它,我使用canvas对象。 I do that by first rendering the graph with it's nodes and links between them, after that I start drawing the person which is represented by a little square moving through the graph. 我首先通过绘制带有节点和它们之间的链接的图形来完成此操作,然后开始绘制一个由在图形中移动的小正方形表示的

My problem is that after I draw the map (or graph) the person's animation deletes the labels and sometimes lines of the graph. 我的问题是,绘制地图(或图形)后,该人的动画会删除标签,有时会删除图形的线条。 I know that this is because I render the person's movement on the same canvas that contains the map (and because of the clearRect() method call). 我知道这是因为我在包含地图的同一画布上渲染了人的运动(并且是由于调用了clearRect()方法)。

How can I avoid clearing the graph?, at first I looked at the JLayeredPane but the canvases overlap and there's no transparency (without trans-parenting the person) on the topmost canvas. 如何避免清除图形?首先,我看了看JLayeredPane但是画布重叠,并且最顶层的画布没有透明度(不对人进行透明转换)。 The second option that I thought of is to copy the area before drawing the person and then restoring the area when the person moves, but I'm not sure how to achieve this so guidance is appreciated as I haven't used swing or awt that much and I think this might be a common problem. 我想到的第二个选择是在绘制人之前先复制区域,然后在人移动时恢复该区域,但是我不确定如何实现这一点,因此我很欣赏指导,因为我没有使用swingawt很多,我认为这可能是一个普遍的问题。

I've attached an image to show my issue and my rendering code for each person 我已附上一张图片,以显示我的问题和每个人的渲染代码

public class Person extends Thread {

    public Person(String name, Spot location, World world, Graphics g) {
    this.name= name;
    this.location= location;
    this.world= world;
    this.g= g;
}

private void move() {
    Set<Link> links= world.getLinksFrom(location.getId());  
Link route= CollectionUtil.getRandomElement(links);  
Spot destination= route.getOriginX() == location.getX() &&   
    route.getOriginY() == location.getY() ?  
    route.getTheTarget(): route.getTheOrigin();  

try {
    double deltaX= (destination.getX() - location.getX()) / route.distance();
    double deltaY= (destination.getY() - location.getY()) / route.distance();
    double w2= (PERSON_WIDTH / 2);

    for(double i=location.getX(), j=location.getY(), d= route.distance(); 
    d > 5;
    i+=deltaX, j+= deltaY, 
    d=Point2D.distance(i, j, destination.getX(), destination.getY())) {
            g.clearRect((int)(i - w2 - deltaX), (int)(j - w2 - deltaY), 
        PERSON_WIDTH, PERSON_WIDTH);
        g.drawRect((int)(i-w2), (int)(j-w2), 
        PERSON_WIDTH-1, PERSON_WIDTH-1);
        Thread.sleep(50);
    }

    this.location= destination;

    // Stay ath the new location for a while
    Thread.sleep(new Random(System.currentTimeMillis()).nextInt(Person.MAX_SPOT_MILLIS));
    } catch(InterruptedException e) {
        throw new RuntimeException(e);
    }

@Override
public void run() {
    while(!isInterrupted()) {
        this.move();            
    }
}
}

我的渲染问题

The two most common solutions are: 两种最常见的解决方案是:

  1. Draw the graph every time. 每次绘制图形。 Use double buffering to avoid flicker. 使用双缓冲以避免闪烁。 Unless your graph has a lot of nodes and you have a very slow computer, this should work well. 除非您的图形有很多节点并且计算机速度很慢,否则这应该工作正常。

  2. Render the graph into an image. 将图形渲染为图像。 Before you start painting the persons, draw the image once. 在开始绘画人物之前,请绘制一次图像。 This clears the whole canvas. 这清除了整个画布。

  1. Unless you create it yourself, you should not be maintaining a reference to the Graphics context. 除非您自己创建它,否则不应维护对Graphics上下文的引用。 The graphics context can be changed between repaints and repaints will override anything painted to it in between. 可以在重绘之间更改图形上下文,并且重绘将覆盖介于两者之间的所有内容。 You should overriding the paintComponet method of a custom component (like JPanel ) and painting the current state of the component on each invocation 您应该重写自定义组件(例如JPanel )的paintComponet方法,并在每次调用时绘制组件的当前状态
  2. It sounds like your are using java.awt.Canvas , you should avoid AWT and use Swing instead. 听起来您正在使用java.awt.Canvas ,应该避免使用AWT,而应使用Swing。 It's a more up to date framework and is double buffered by default. 这是一个更新的框架,默认情况下是双缓冲的。 You should avoid mixing AWT and Swing components as they tend not to play nice together 您应该避免混合使用AWT和Swing组件,因为它们往往不能很好地配合使用
  3. Swing is a single threaded environment, meaning that you should only interact with the UI from within the context of the Event Dispatching Thread? Swing是一个单线程环境,这意味着您应该只从事件调度线程的上下文中与UI交互? Check out Concurrency in Swing for more details. 在Swing中查看并发以获取更多详细信息。
  4. There are 3 ways to do animation Swing. 有3种制作动画Swing的方法。 You can use a Thread or a javax.swing.Timer or one of the animation frameworks. 您可以使用Threadjavax.swing.Timer或动画框架之一。

Using Threads is more complex and makes you responsible for ensuring that updates are correctly resynced by to the client. 使用线程更加复杂,使您有责任确保更新正确地重新同步到客户端。 This is useful for complex animation jobs, where the time between calculating the state on each cycle my vary. 这对于复杂的动画作业很有用,因为在每个动画周期中计算状态之间的时间会有所不同。

javax.swing.Timer is simpler, as it's callback is executed within the context of the EDT at regular intervals. javax.swing.Timer更简单,因为它的回调是在EDT的上下文中定期执行的。 It's great for simple animations where the time it takes to update the state are short and/or reasonable constant so that they won't have the potential of causing staggering frames 这对于简单的动画非常有用,因为更新状态所需的时间很短和/或具有合理的常数,因此它们不会引起交错帧的可能性

If you want to try having two canvases then I think I may have a solution. 如果您想尝试使用两个画布,那么我想我可以找到一个解决方案。

Create your first canvas that will draw your graph and create a canvas on top of that that will draw your people. 创建您的第一个画布来绘制图形,并在其上创建一个画布来绘制您的人员。

You can set the background colour of the top canvas to be a clear colour and then also clear the canvas with that same clear colour. 您可以将顶部画布的背景色设置为纯色,然后再使用相同的纯色来清理画布。

For example: 例如:

g.setBackground(new Color(0,0,0,0));
g.clearRect(0, 0, width, height);

will clear the top canvas leaving a completely transparent colour behind 将清除顶部画布,在后面留下完全透明的颜色

canvas.setBackground(new Color(0,0,0,0);

will make the canvas component transparent. 将使画布组件透明。 This only needs to be called once when you are creating the canvas. 创建画布时,只需调用一次即可。

Now the top canvas will be completely transparent after your clear it with the 0 alpha colour and only what you draw on the top canvas will block out the bottom canvas. 现在,使用0 alpha颜色清除顶部画布后,顶部画布将完全透明,并且只有在顶部画布上绘制的内容才会遮挡底部画布。

Hope this helps 希望这可以帮助

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

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