ajb*_*ajb 3 java graphics swing graphics2d
我正试图在Swing中完成图形内容(绘制线条等).到目前为止,我所见过的所有教程都声明了一个覆盖的类paintComponent,并且所有paintComponent方法都做了一些设置,特定的事情,比如绘制一个红色方块(尽管可能每次都在不同的位置绘制它).或者他们可能会绘制一些线条和形状,但该paintComponent方法可以同时完成所有操作.
我想弄清楚:假设我想在一个组件中绘制一个东西,然后在它上面绘制其他东西而不删除我之前绘制的东西.我的第一个想法是让我的
paintComponent覆盖调用回调.
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();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
这不起作用.出现蓝线,但没有红线.我猜这是因为当我绘制蓝线时,repaint()in draw会导致一切都重新开始,但我不确定; 无论如何,我不知道怎么回事
paintComponent.
另外,如果我Thread.sleep(1000)在两个p.draw电话之间放一个,我甚至都看不到红线.因此,我并不清楚如何在我想要的时候显示我的图形.
我已经在Swing中对"增量图形"进行了一些搜索,但没有任何东西可以帮助找到解决方案.我发现了一篇Oracle文章"在AWT和Swing中绘画",讨论了覆盖一种update()
实现增量图形的方法,但我还没有找到任何实际的例子.
那么我怎么能做到这一点呢?这似乎是一个很常见的任务,应该有一个简单的方法来做,但我还没有找到一个.我假设它应该可以在没有调用的情况下完成
getGraphics,基于其他StackOverflow响应,它充其量只是一种诡计.
Swing中的绘画具有破坏性.也就是说,每当新的绘制周期运行时,您都应该根据要绘制的对象的状态完全重建输出.
所以当你打电话
p.draw (new GraphicsAction () {
public void action (Graphics g) {
g.setColor(Color.RED);
g.drawLine(5, 30, 100, 50);
}
});
Run Code Online (Sandbox Code Playgroud)
其次是
p.draw (new GraphicsAction () {
public void action (Graphics g) {
g.setColor(Color.BLUE);
g.drawLine(5, 30, 150, 40);
}
});
Run Code Online (Sandbox Code Playgroud)
你基本上扔掉了第一个动作.忽略目前如何安排重拍.第一个请求说"画一条红线",第二个请求"画一条蓝线",但在执行这些动作之前,Graphics上下文被清理,准备更新.
这非常重要,因为Graphics您提供的上下文是共享资源.之前绘制的所有组件都使用了相同的上下文,在您使用相同的上下文后绘制的所有组件.这意味着,如果在绘制之前没有"清理"上下文,则最终会出现不需要的绘制工件.
但你怎么能绕过它?
这里有一些选择.
您可以绘制到BufferedImage具有自己的Graphics上下文的后备缓冲区(或),您可以添加它,并且只需要在paintComponent方法中"绘制" .
这意味着,每次调用时p.draw(...),您实际上都会首先绘制到此缓冲区然后调用repaint.
这个问题是,你需要保持缓冲区的大小.每次组件大小更改时,您都需要根据组件的新大小将此缓冲区复制到新缓冲区.这有点乱,但是可行.
另一个解决方案是将每个操作放在一个List并且在需要时,只需循环遍历并在需要时List重新应用操作.
这可能是最简单的方法,但随着动作数量的增加,可能会降低油漆过程的有效性,从而减缓油漆过程.
您也可以使用两者的组合.当缓冲区不存在时生成缓冲区,遍历List操作并将它们渲染到缓冲区并简单地在paintComponent方法中绘制缓冲区.每当组件调整大小时,只需null缓冲区并允许paintComponent再生它......例如......
另外,如果我在两个p.draw调用之间放置一个Thread.sleep(1000)
Swing是一个单线程框架.这意味着所有更新和修改都应在Event Dispatching Thread的上下文中完成.
同样,任何阻止EDT运行的东西都会阻止它处理(除其他外)绘制请求.
这意味着当您sleep在p.draw两次调用之间时,您正在停止运行EDT,这意味着它无法处理您的绘制请求...
有关更多详细信息,请参阅Swing中的Concurrency
更新了示例

我只想指出这是非常低效的.每次invalidate调用时重新创建缓冲区将创建大量短期对象,并且可能会显着降低性能.
通常情况下,我会使用a 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;
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();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1342 次 |
| 最近记录: |