即使在使用Java
Swing一年以上之后,对我来说,它仍然像魔术一样。如何正确使用BufferStrategy,尤其是方法createBufferSrategy()
?
我想添加一个JFrame和一个Canvas,然后进行绘制。我还希望能够调整(setSize()
)画布的大小。每次我调整Canvas的大小时,似乎BufferStrategy
都会被浪费掉,或者变得毫无用处,因为show()
在上使用BufferStrategy
并没有真正做任何事情。另外,createBufferStrategy()
它具有怪异的不确定性行为,我不知道如何正确同步它。
这就是我的意思:
public class MyFrame extends JFrame {
MyCanvas canvas;
int i = 0;
public MyFrame() {
setUndecorated(false);
setVisible(true);
setSize(1100, 800);
setLocation(100, 100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
canvas = new MyCanvas();
add(canvas);
canvas.makeBufferStrat();
}
@Override
public void repaint() {
super.repaint();
canvas.repaint();
//the bigger threshold's value, the more likely it is that the BufferStrategy works correctly
int threshold = 2;
if (i < threshold) {
i++;
canvas.makeBufferStrat();
}
}
}
MyCanvas
有一个方法makeBufferStrat()
和repaint()
:
public class MyCanvas extends Canvas {
BufferStrategy bufferStrat;
Graphics2D g;
public MyCanvas() {
setSize(800, 600);
setVisible(true);
}
public void makeBufferStrat() {
createBufferStrategy(2);
//I'm not even sure whether I need to dispose() those two.
if (g != null) {
g.dispose();
}
if (bufferStrat != null) {
bufferStrat.dispose();
}
bufferStrat = getBufferStrategy();
g = (Graphics2D) (bufferStrat.getDrawGraphics());
g.setColor(Color.BLUE);
}
@Override
public void repaint() {
g.fillRect(0, 0, 100, 100);
bufferStrat.show();
}
}
我只需拨打MyFrame
的repaint()
从main方法的同时(true)循环方法。当threshold
它很小(即2)时,bufferStrat.show()
在所有情况下大约70%都不做任何事情-
JFrame在启动程序时只是保持灰色。剩余的30%会按照预期方式绘制矩形。如果我这样做threshold = 200;
,则绘画成功执行我的程序的时间接近100%。Javadoc说createBufferStrategy()
可能要花一些时间,所以我认为这就是问题所在。但是,如何正确同步和使用它?显然,我在这里做错了。我无法想象这就是应该使用的方式。
有谁有一个简单的工作例子?
创建的方式BufferStrategy
是“可以的”,您可以看看JavaDocs的BufferStrategy
示例,它非常简洁。
您使用它的方式令人怀疑。使用a的主要原因BufferStrategy
是因为您希望控制绘画过程(主动绘画)而不是Swing的绘画算法(是被动的)
但是,您似乎试图同时做这两个事情,这就是为什么它会引起您的问题。相反,您应该有一个“主”循环,该循环负责决定缓冲区应绘制的内容和时间,例如…
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferStrategy;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
TestPane testPane = new TestPane();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(testPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// The component needs to be attached to displayed window before
// the buffer can be created
testPane.startPainting();
}
});
}
public class TestPane extends Canvas {
private AtomicBoolean painting = new AtomicBoolean(true);
private PaintCycle paintCycle;
private Rectangle clickBounds;
public TestPane() {
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (clickBounds != null && clickBounds.contains(e.getPoint())) {
painting.set(false);
}
}
});
}
public void startPainting() {
if (paintCycle == null) {
createBufferStrategy(2);
painting.set(true);
paintCycle = new PaintCycle();
Thread t = new Thread(paintCycle);
t.setDaemon(true);
t.start();
}
}
public void stopPainting() {
if (paintCycle != null) {
painting.set(false);
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public class PaintCycle implements Runnable {
private BufferStrategy strategy;
private int xDelta = 2;
private int yDelta = 2;
@Override
public void run() {
System.out.println("Painting has started");
int x = (int) (Math.random() * (getWidth() - 40));
int y = (int) (Math.random() * (getHeight() - 40));
do {
xDelta = (int) (Math.random() * 8) - 4;
} while (xDelta == 0);
do {
yDelta = (int) (Math.random() * 8) - 4;
} while (yDelta == 0);
clickBounds = new Rectangle(x, y, 40, 40);
strategy = getBufferStrategy();
while (painting.get()) {
// Update the state of the model...
update();
// Paint the state of the model...
paint();
try {
// What ever calculations you want to use to maintain the framerate...
Thread.sleep(40);
} catch (InterruptedException ex) {
}
}
System.out.println("Painting has stopped");
}
protected void update() {
int x = clickBounds.x + xDelta;
int y = clickBounds.y + yDelta;
if (x + 40 > getWidth()) {
x = getWidth() - 40;
xDelta *= -1;
} else if (x < 0) {
x = 0;
xDelta *= -1;
}
if (y + 40 > getHeight()) {
y = getHeight() - 40;
yDelta *= -1;
} else if (y < 0) {
y = 0;
yDelta *= -1;
}
clickBounds.setLocation(x, y);
}
protected void paint() {
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics2D graphics = (Graphics2D) strategy.getDrawGraphics();
// Render to graphics
// ...
graphics.setColor(Color.BLUE);
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.setColor(Color.RED);
graphics.fill(clickBounds);
// Dispose the graphics
graphics.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
}
}
您还应该记住,Swing从大约1.4(或者可能是1.5)开始就一直使用DirectX或OpenGL管道。使用的主要原因BufferStrategy
是对硬件的直接访问(无论如何,Swing都非常接近)以及对绘画过程的直接控制(现在实际上是唯一使用它的原因)
问题内容: 我想在Linux上使用该机制。我希望我的应用程序知道何时更改了文件。能否请您提供给我一个示例,该怎么做? 问题答案: 文档(来自具有inotify的Monitor文件系统活动) 在C API 提供了三个系统调用来构建各种文件系统监视器: 在内核中创建子系统的实例,并在成功和失败时返回文件描述符。与其他系统调用一样,如果失败,请检查诊断。 顾名思义,它增加了一块 手表 。每个监视都必须提
问题内容: 需要帮助,以了解如何在UIKit中使用prepareForReuse()。该文件说 您只应重置与内容无关的单元格属性,例如Alpha,编辑和选择状态 但是如何重置单个属性属性(例如isHidden)呢? 假设我的单元格有2个标签,我应该在哪里重置: 标签文本 label.numberOfLines label.isHidden 我的tableView(_:cellForRowAt :)
一段时间以来,我一直试图将我的tableview工作作为一种电子表格,通过背景线程进行更新,当单元格更新时,它会亮起几秒钟(更改样式),然后返回到原始样式。我已经知道,我不能直接在表格单元格中存储和设置样式,我需要某种支持类来保存这些数据。但是tableview“重用”单元格(使用相同的单元格处理不同的数据)的行为真的很奇怪。当所有单元格都适合屏幕时,它对我来说完美无瑕,但一旦我放置大约100个单
那么,回到问题上来,鉴于微软建议不要在新的开发中使用WebRequest而是使用只提供异步API的HttpClient,我该怎么办呢? 这是我正在做的一些伪代码... 如何实现ProcessStuff()?
问题内容: 我正在与我的CompSci教授交谈,他建议将所有String 方法编写为: 而不是: 这两行都可以编译,但是我想知道第一种方法的好处是什么?我一直都是后一种方式。错了吗 什么是普通/常规? 问题答案: 第一种方法确保执行比较时不会收到 NullPointerException 。当您尝试在不存在的对象上调用方法时,抛出(发生)此异常。 以下是一些相关的切线:仔细阅读风险自负 不过要注意
问题内容: 我在一些帖子中看到人们在编写动态HTML时不喜欢在javascript中使用。 为什么是这样?什么是 正确的 方法是什么? 问题答案: 仅在最初解析页面和创建DOM时才起作用。浏览器到达结束标记并且DOM准备就绪后,就不能再使用了。 我不会说使用是正确的还是不正确的,这仅取决于您的情况。在某些情况下,您只需要完成任务即可。查看如何将Google Analytics(分析)注入大多数网站