Swing中的增量图形 [英] Incremental graphics in Swing
问题描述
我试图在Swing中做图形的东西(绘制线,
等)。到目前为止,我见过的所有教程都声明了一个覆盖 paintComponent
的类
,并且所有的 paintComponent
方法
做一些特定的事情,比如绘制一个红色方块(尽管可能每
在不同的位置绘制它们)。或者,他们可以绘制多条线条和形状,但 paintComponent
方法一次完成所有工作。
I试图弄清楚:假设我想在一个
组件中绘制一个东西,然后在其上绘制其他东西,而不用
擦除我之前绘制的东西。我的第一个想法是让我的
paintComponent
覆盖调用回调函数。
import java.awt。*;
import javax.swing。*;
public class DrawTest {
private interface GraphicsAction {
public void action(Graphics g);
}
私有静态类TestPanel扩展JPanel {
私有GraphicsAction paintAction;
public void draw(GraphicsAction action){
paintAction = action;
repaint();
}
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
if(paintAction!= null)
paintAction.action(g);
private static void createAndShowGui(){
JFrame frame = new JFrame(DrawTest);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(500,500));
TestPanel p = new TestPanel();
frame.getContentPane()。add(p);
frame.pack();
frame.setVisible(true);
p.repaint();
p.draw(new GraphicsAction(){
public void action(Graphics g){
g.setColor(Color.RED);
g.drawLine 5,30,100,50);
}
});
//在现实生活中,我们会做一些其他的东西,然后
//稍后有些东西想要为
添加一条蓝线// //
p.draw(new GraphicsAction(){
public void action(Graphics g){
g.setColor(Color.BLUE);
g.drawLine(5,30, 150,40);
}
});
$ b public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
createAndShowGui();
}
});
}
}
这是行不通的。蓝线出现,但没有红线。我是
猜测这是因为中的
会导致所有 repaint()
绘制
到当我画蓝线时重新开始,但我不确定;无论如何,我不知道如何去调用
paintComponent
。
另外,如果我在两个 p.draw
调用之间放置 Thread.sleep(1000)
,则
I don一秒钟都看不到红线。所以我完全不清楚
如何让我的图形出现在我想要的时候。
我已经在增量图形在Swing中,但
没有什么能帮助找到解决方案。我发现一篇Oracle文章
在AWT和Swing中绘画,讨论了重写一个 update()
方法来完成增量图形,但我没有' t找到了任何
的实际例子。
那么我怎样才能做到我想要的?这似乎是一个常见的
足够的任务,应该有一个简单的方法来做到这一点,但我没有找到一个
。我假设它不应该调用
getGraphics
,它基于其他的StackOverflow响应,最好是
。
Swing中的绘画是破坏性的。也就是说,每当一个新的绘画循环运行时,你应该根据你正在绘画的对象的状态完全重建输出。
所以,当你调用
p.draw(new GraphicsAction(){
public void action(Graphics g){
g.setColor(Color.RED);
g.drawLine(5,30,100,50);
}
});
紧接着
p.draw(new GraphicsAction(){
public void action(Graphics g){
g.setColor(Color.BLUE);
g.drawLine(5,30 ,150,40);
}
});
您基本上会抛弃第一个操作。忽略当前计划重绘的方式。第一个请求表示绘制红线,第二个请求表示绘制蓝线,但在执行这些动作之前,清理 Graphics
上下文,准备它用于更新。
这非常重要,因为您提供的 Graphics
上下文是共享资源。之前绘制的所有组件都使用相同的上下文,在使用相同的上下文之后绘制所有组件。这意味着,如果您在绘制上下文之前没有清理上下文,则最终会产生不需要的绘制伪像。
但是,如何避免这种情况? ?
这里有几个选择。
您可以绘制到后台缓冲区(或 这意味着,每次调用 有了这个,你需要保持缓冲区的大小。每次组件大小发生更改时,都需要根据组件的新大小将此缓冲区复制到新缓冲区。这是一个有点混乱,但是可行的。 另一个解决方案是将每个动作放在 这可能是最简单的方法,但随着行动数量的增加,可能会降低油漆过程的效率,减慢油漆过程。 您也可以将两者结合使用。当它不存在时生成一个缓冲区,遍历操作的 另外,如果我在两个p.draw调用之间放置Thread.sleep(1000) Swing是一个单线程框架。这意味着所有的更新和修改都要在Event Dispatching Thread的上下文中完成。同样,阻止EDT运行的任何东西都会阻止它进行处理( $ b 这意味着当 看看 Swing的并发性了解更多详情 以示例更新 我只想指出这真的是效率低下。每次调用 通常,我会使用 I'm trying to get the hang of doing graphics stuff (drawing lines,
etc.) in Swing. So far, all the tutorials I've seen declare a class
that overrides I'm trying to figure out: suppose I want to draw one thing in a
component, and later on draw something else on top of it without
erasing what I drew before. My first thought was to have my
This doesn't work. A blue line shows up, but no red line. I'm
guessing this is because the Also, if I put a I've done some searching on "incremental graphics" in Swing, but
nothing that helps find a solution. I found an Oracle article
"Painting in AWT and Swing" that discusses overriding an So how would I get this to do what I want? It seems like a common
enough task that there should be a simple way to do it, but I haven't
found one. I'm assuming it should be doable without calling
Painting in Swing is destructive. That is, whenever a new paint cycle runs, you are expected to completely rebuild the output as per the state of the object you are painting. Take a look at Painting in AWT and Swing So when you call Followed by You are basically throwing away the first action. Ignoring how repaints are scheduled for the moment. The first request says, "paint a red line", the second says, "paint a blue line", but before these actions are executed, the This is very important, as the But how can you get around it?? There are a few choices here. You could draw to a backing buffer (or This would mean, each time you call The problem with this, is you need to maintain the size of the buffer. Each time the component size changes, you would need to copy this buffer to a new buffer based on the new size of the component. This is a little messy, but is doable. The other solution would be to place each action in a This is probably the simplest approach, but as the number of actions grow, could reduce the effectiveness of the paint process, slowing the paint process. You could also use a combination of the two. Generate a buffer when it doesn't exists, loop through the Also, if I put a Thread.sleep(1000) between the two p.draw calls Swing is a single threaded framework. That means that all updates and modifications are expected to done within the context of the Event Dispatching Thread. Equally, anything that blocks the EDT from running will prevent it from process (amongst other things) paint requests. This means that when you Take a look at Concurrency in Swing for more details Updated with example I just want to point out that is really inefficient. Re-creating the buffer each time Normally, I would use a
这篇关于Swing中的增量图形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! BufferedImage
),它有它自己的 Graphics context,你可以添加并且只需要在你的<$ c
$ b $ p p.draw(...)时,
,你实际上会先画到这个缓冲区,然后调用 repaint
。
List
在需要时,只需循环 List
并在需要时重新应用该操作。
List
并将它们渲染到缓冲区,并简单地在 paintComponent
方法。无论何时调整组件大小,只需 null
缓冲区并允许 paintComponent
重新生成它...例如.. 。
> sleep
在 p之间时.draw
调用,你停止运行EDT,这意味着它无法处理你的绘画请求...
invalidate
时重新创建缓冲区将会创建大量短暂的对象,并可能大大降低性能。
javax.swing.Timer
,设置为不重复的,每次 invalidate时都会重新启动
被调用。这将设置为一个短暂的延迟(大约在125-250毫秒之间)。当计时器被触发时,我只是简单地在这个时候重新构造缓冲区,但这只是一个例子;)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
导入javax.swing.SwingUtilities;
public class DrawTest {
private interface GraphicsAction {
public void action(Graphics g);
}
私有静态类TestPanel扩展JPanel {
私有GraphicsAction paintAction;
专用BufferedImage缓冲区;
@Override
public void invalidate(){
BufferedImage img = new BufferedImage(
Math.max(1,getWidth()),
Math .max(1,getHeight()),BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(getBackground());
g2d.fillRect(0,0,getWidth(),getHeight());
if(buffer!= null){
g2d.drawImage(buffer,0,0,this);
}
g2d.dispose();
buffer = img;
super.invalidate(); (
)
保护BufferedImage getBuffer(){
if(buffer == null){
buffer = new BufferedImage(
Math.max(1, getWidth()),
Math.max(1,getHeight()),BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
g2d.setColor(getBackground());
g2d.fillRect(0,0,getWidth(),getHeight());
g2d.dispose();
}
返回缓冲区;
}
public void draw(GraphicsAction action){
BufferedImage buffer = getBuffer();
Graphics2D g2d = buffer.createGraphics();
action.action(g2d);
g2d.dispose();
repaint();
}
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(getBuffer(),0,0,this);
private static void createAndShowGui(){
JFrame frame = new JFrame(DrawTest);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(500,500));
TestPanel p = new TestPanel();
frame.getContentPane()。add(p);
frame.pack();
frame.setVisible(true);
p.repaint();
p.draw(new GraphicsAction(){
public void action(Graphics g){
g.setColor(Color.RED);
g.drawLine 5,30,100,50);
}
});
//在现实生活中,我们会做一些其他的东西,然后
//稍后有些东西想要为
添加一条蓝线// //
p.draw(new GraphicsAction(){
public void action(Graphics g){
g.setColor(Color.BLUE);
g.drawLine(5,30,150,40) ;
}
});
$ b public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
createAndShowGui();
}
});
}
}
paintComponent
, and all the paintComponent
methods
do some set, specific thing, like drawing a red square (although maybe
they draw it at a different location every time). Or maybe they draw a number of lines and shapes, but the paintComponent
method does everything all at once.paintComponent
override call a callback.import java.awt.*;
import javax.swing.*;
public class DrawTest {
private interface GraphicsAction {
public void action (Graphics g);
}
private static class TestPanel extends JPanel {
private GraphicsAction paintAction;
public void draw (GraphicsAction action) {
paintAction = action;
repaint();
}
@Override
public void paintComponent (Graphics g) {
super.paintComponent (g);
if (paintAction != null)
paintAction.action(g);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame ("DrawTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(500,500));
TestPanel p = new TestPanel ();
frame.getContentPane().add(p);
frame.pack();
frame.setVisible(true);
p.repaint();
p.draw (new GraphicsAction () {
public void action (Graphics g) {
g.setColor(Color.RED);
g.drawLine(5, 30, 100, 50);
}
});
// in real life, we would do some other stuff and then
// later something would want to add a blue line to the
// diagram
p.draw (new GraphicsAction () {
public void action (Graphics g) {
g.setColor(Color.BLUE);
g.drawLine(5, 30, 150, 40);
}
});
}
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGui();
}
});
}
}
repaint()
in draw
causes everything
to start over when I draw the blue line, but I'm not sure; anyway, I don't know how else to get
paintComponent
to be called.Thread.sleep(1000)
between the two p.draw
calls,
I don't even see the red line for a second. So I'm not at all clear
on how to get my graphics to show up when I want them to.update()
method to accomplish incremental graphics, but I haven't found any
actual examples of this being done.getGraphics
, which, based on other StackOverflow responses would be,
at best, kind of gauche.p.draw (new GraphicsAction () {
public void action (Graphics g) {
g.setColor(Color.RED);
g.drawLine(5, 30, 100, 50);
}
});
p.draw (new GraphicsAction () {
public void action (Graphics g) {
g.setColor(Color.BLUE);
g.drawLine(5, 30, 150, 40);
}
});
Graphics
context is cleaned, preparing it for updating.Graphics
context you are provided is a shared resource. All the components painted before have used the same context, all the components painted after you will use the same context. This means, if you don't "clean" the context before painting to it, you can end up with unwanted paint artifacts.BufferedImage
) which has it's own Graphics
context, which you can add to and would only need to "paint" in your paintComponent
method.p.draw(...)
, you would actually paint to this buffer first then call repaint
.List
and when required, simply loop through the List
and re-apply the action whenever required.List
of actions and renderer them to the buffer and simply paint the buffer in the paintComponent
method. Whenever the component is resized, simply null
the buffer and allow the paintComponent
to regenerate it...for example...
sleep
between the p.draw
calls, you are stopping the EDT from running, meaning it can't process your paint requests...invalidate
is called will create a large number of short lived objects and could a significant drain on performance.javax.swing.Timer
, set to be non-repeating, that would be restarted each time invalidate
was called. This would be set to a short delay (somewhere between 125-250 milliseconds). When the timer is triggered, I would simply re-construct the buffer at this time, but this is just an example ;)import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawTest {
private interface GraphicsAction {
public void action(Graphics g);
}
private static class TestPanel extends JPanel {
private GraphicsAction paintAction;
private BufferedImage buffer;
@Override
public void invalidate() {
BufferedImage img = new BufferedImage(
Math.max(1, getWidth()),
Math.max(1, getHeight()), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
if (buffer != null) {
g2d.drawImage(buffer, 0, 0, this);
}
g2d.dispose();
buffer = img;
super.invalidate();
}
protected BufferedImage getBuffer() {
if (buffer == null) {
buffer = new BufferedImage(
Math.max(1, getWidth()),
Math.max(1, getHeight()), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
return buffer;
}
public void draw(GraphicsAction action) {
BufferedImage buffer = getBuffer();
Graphics2D g2d = buffer.createGraphics();
action.action(g2d);
g2d.dispose();
repaint();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(getBuffer(), 0, 0, this);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(500, 500));
TestPanel p = new TestPanel();
frame.getContentPane().add(p);
frame.pack();
frame.setVisible(true);
p.repaint();
p.draw(new GraphicsAction() {
public void action(Graphics g) {
g.setColor(Color.RED);
g.drawLine(5, 30, 100, 50);
}
});
// in real life, we would do some other stuff and then
// later something would want to add a blue line to the
// diagram
p.draw(new GraphicsAction() {
public void action(Graphics g) {
g.setColor(Color.BLUE);
g.drawLine(5, 30, 150, 40);
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGui();
}
});
}
}