[英]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. 我想到的第二个选择是在绘制人之前先复制区域,然后在人移动时恢复该区域,但是我不确定如何实现这一点,因此我很欣赏指导,因为我没有使用
swing
或awt
很多,我认为这可能是一个普遍的问题。
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: 两种最常见的解决方案是:
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.
除非您的图形有很多节点并且计算机速度很慢,否则这应该工作正常。
Render the graph into an image. 将图形渲染为图像。 Before you start painting the persons, draw the image once.
在开始绘画人物之前,请绘制一次图像。 This clears the whole canvas.
这清除了整个画布。
Graphics
context. Graphics
上下文的引用。 The graphics context can be changed between repaints and repaints will override anything painted to it in between. paintComponet
method of a custom component (like JPanel
) and painting the current state of the component on each invocation JPanel
)的paintComponet
方法,并在每次调用时绘制组件的当前状态 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. Thread
or a javax.swing.Timer
or one of the animation frameworks. Thread
或javax.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.